Sage 9.1 Reference Manual: Cryptography: Release 9.1
Sage 9.1 Reference Manual: Cryptography: Release 9.1
1 Reference Manual:
Cryptography
Release 9.1
1 Cryptosystems 1
2 Ciphers 5
3 Classical Cryptosystems 7
4 Classical Ciphers 41
5 Simplified DES 43
6 Mini-AES 53
7 DES 73
8 PRESENT 81
10 Stream Cryptosystems 99
18 Small Scale Variants of the AES (SR) Polynomial System Generator 157
19 Rijndael-GF 187
i
Python Module Index 223
Index 225
ii
CHAPTER
ONE
CRYPTOSYSTEMS
This module contains base classes for various cryptosystems, including symmetric key and public-key cryptosystems.
The classes defined in this module should not be called directly. It is the responsibility of child classes to implement
specific cryptosystems. Take for example the Hill or matrix cryptosystem as implemented in HillCryptosystem.
It is a symmetric key cipher so HillCryptosystem is a child class of SymmetricKeyCryptosystem, which
in turn is a child class of Cryptosystem. The following diagram shows the inheritance relationship of particular
cryptosystems:
Cryptosystem
+ SymmetricKeyCryptosystem
| + HillCryptosystem
| + LFSRCryptosystem
| + ShiftCryptosystem
| + ShrinkingGeneratorCryptosystem
| + SubstitutionCryptosystem
| + TranspositionCryptosystem
| + VigenereCryptosystem
+ PublicKeyCryptosystem
𝐸 : 𝒦 → Hom(ℳ, 𝒞)
𝐷 : 𝒦 → Hom(𝒞, ℳ)
where 𝒦 is the key space, ℳ is the plaintext or message space, and 𝒞 is the ciphertext space. In many instances
ℳ = 𝒞 and the images will lie in Aut(ℳ). An element of the image of 𝐸 is called a cipher.
We may assume that 𝐸 and 𝐷 are injective, hence identify a key 𝐾 in 𝒦 with its image 𝐸𝐾 := 𝐸(𝐾) in
Hom(ℳ, 𝒞).
The cryptosystem has the property that for every encryption key 𝐾1 there is a decryption key 𝐾2 such that
𝐷𝐾2 ∘ 𝐸𝐾1 . A cryptosystem with the property that 𝐾 := 𝐾2 = 𝐾1 , is called a symmetric cryptosystem.
Otherwise, if the key 𝐾2 ̸= 𝐾1 , nor is 𝐾2 easily derived from 𝐾1 , we call the cryptosystem asymmetric or
public key. In that case, 𝐾1 is called the public key and 𝐾2 is called the private key.
INPUT:
• plaintext_space – the plaintext alphabet.
• ciphertext_space – the ciphertext alphabet.
1
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: ShiftCryptosystem(AlphabeticStrings())
Shift cryptosystem on Free alphabetic string monoid on A-Z
sage: SubstitutionCryptosystem(HexadecimalStrings())
Substitution cryptosystem on Free hexadecimal string monoid
sage: HillCryptosystem(BinaryStrings(), 3)
Hill cryptosystem on Free binary string monoid of block length 3
sage: TranspositionCryptosystem(OctalStrings(), 5)
Transposition cryptosystem on Free octal string monoid of block length 5
sage: VigenereCryptosystem(Radix64Strings(), 7)
Vigenere cryptosystem on Free radix 64 string monoid of period 7
block_length()
Return the block length of this cryptosystem. For some cryptosystems this is not relevant, in which case
the block length defaults to 1.
EXAMPLES:
The block lengths of various classical cryptosystems:
sage: ShiftCryptosystem(AlphabeticStrings()).block_length()
1
sage: SubstitutionCryptosystem(HexadecimalStrings()).block_length()
1
sage: HillCryptosystem(BinaryStrings(), 3).block_length()
3
sage: TranspositionCryptosystem(OctalStrings(), 5).block_length()
5
sage: VigenereCryptosystem(Radix64Strings(), 7).block_length()
1
cipher_codomain()
Return the alphabet used by this cryptosystem for encoding ciphertexts. This is the same as the ciphertext
space.
EXAMPLES:
The cipher codomains, or ciphertext spaces, of various classical cryptosystems:
sage: ShiftCryptosystem(AlphabeticStrings()).cipher_codomain()
Free alphabetic string monoid on A-Z
sage: SubstitutionCryptosystem(HexadecimalStrings()).cipher_codomain()
Free hexadecimal string monoid
sage: HillCryptosystem(BinaryStrings(), 3).cipher_codomain()
Free binary string monoid
sage: TranspositionCryptosystem(OctalStrings(), 5).cipher_codomain()
Free octal string monoid
sage: VigenereCryptosystem(Radix64Strings(), 7).cipher_codomain()
Free radix 64 string monoid
cipher_domain()
Return the alphabet used by this cryptosystem for encoding plaintexts. This is the same as the plaintext
2 Chapter 1. Cryptosystems
Sage 9.1 Reference Manual: Cryptography, Release 9.1
space.
EXAMPLES:
The cipher domains, or plaintext spaces, of various classical cryptosystems:
sage: ShiftCryptosystem(AlphabeticStrings()).cipher_domain()
Free alphabetic string monoid on A-Z
sage: SubstitutionCryptosystem(HexadecimalStrings()).cipher_domain()
Free hexadecimal string monoid
sage: HillCryptosystem(BinaryStrings(), 3).cipher_domain()
Free binary string monoid
sage: TranspositionCryptosystem(OctalStrings(), 5).cipher_domain()
Free octal string monoid
sage: VigenereCryptosystem(Radix64Strings(), 7).cipher_domain()
Free radix 64 string monoid
ciphertext_space()
Return the ciphertext alphabet of this cryptosystem.
EXAMPLES:
The ciphertext spaces of various classical cryptosystems:
sage: ShiftCryptosystem(AlphabeticStrings()).ciphertext_space()
Free alphabetic string monoid on A-Z
sage: SubstitutionCryptosystem(HexadecimalStrings()).ciphertext_space()
Free hexadecimal string monoid
sage: HillCryptosystem(BinaryStrings(), 3).ciphertext_space()
Free binary string monoid
sage: TranspositionCryptosystem(OctalStrings(), 5).ciphertext_space()
Free octal string monoid
sage: VigenereCryptosystem(Radix64Strings(), 7).ciphertext_space()
Free radix 64 string monoid
key_space()
Return the alphabet used by this cryptosystem for encoding keys.
EXAMPLES:
The key spaces of various classical cryptosystems:
sage: ShiftCryptosystem(AlphabeticStrings()).key_space()
Ring of integers modulo 26
sage: SubstitutionCryptosystem(HexadecimalStrings()).key_space()
Free hexadecimal string monoid
sage: HillCryptosystem(BinaryStrings(), 3).key_space()
Full MatrixSpace of 3 by 3 dense matrices over Ring of integers modulo 2
sage: TranspositionCryptosystem(OctalStrings(), 5).key_space()
Symmetric group of order 5! as a permutation group
sage: VigenereCryptosystem(Radix64Strings(), 7).key_space()
Free radix 64 string monoid
period()
plaintext_space()
Return the plaintext alphabet of this cryptosystem.
EXAMPLES:
The plaintext spaces of various classical cryptosystems:
3
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: ShiftCryptosystem(AlphabeticStrings()).plaintext_space()
Free alphabetic string monoid on A-Z
sage: SubstitutionCryptosystem(HexadecimalStrings()).plaintext_space()
Free hexadecimal string monoid
sage: HillCryptosystem(BinaryStrings(), 3).plaintext_space()
Free binary string monoid
sage: TranspositionCryptosystem(OctalStrings(), 5).plaintext_space()
Free octal string monoid
sage: VigenereCryptosystem(Radix64Strings(), 7).plaintext_space()
Free radix 64 string monoid
sage: ShiftCryptosystem(AlphabeticStrings()).alphabet_size()
26
sage: ShiftCryptosystem(BinaryStrings()).alphabet_size()
2
sage: ShiftCryptosystem(HexadecimalStrings()).alphabet_size()
16
sage: SubstitutionCryptosystem(OctalStrings()).alphabet_size()
8
sage: SubstitutionCryptosystem(Radix64Strings()).alphabet_size()
64
4 Chapter 1. Cryptosystems
CHAPTER
TWO
CIPHERS
5
Sage 9.1 Reference Manual: Cryptography, Release 9.1
6 Chapter 2. Ciphers
CHAPTER
THREE
CLASSICAL CRYPTOSYSTEMS
𝑐 ≡ 𝑎𝑝 + 𝑏 (mod 𝑛)
7
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Similarly, given a ciphertext character 𝑐 ∈ Z/𝑛Z and a secret key (𝑎, 𝑏), we can recover the corresponding
plaintext character as follows:
𝑝 ≡ 𝑎−1 (𝑐 − 𝑏) (mod 𝑛)
where 𝑎−1 is the inverse of 𝑎 modulo 𝑛. Use the bijection 𝑓 : 𝐴 −→ Z/𝑛Z to convert 𝑐 and 𝑝 back to elements
of the alphabet 𝐴. Currently, only the following alphabet is supported for the affine cipher:
• capital letters of the English alphabet as implemented in AlphabeticStrings()
EXAMPLES:
Encryption and decryption over the capital letters of the English alphabet:
sage: A = AffineCryptosystem(AlphabeticStrings()); A
Affine cryptosystem on Free alphabetic string monoid on A-Z
sage: P = A.encoding("The affine cryptosystem generalizes the shift cipher.")
sage: P
THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
sage: a, b = (9, 13)
sage: C = A.enciphering(a, b, P); C
CYXNGGHAXFKVSCJTVTCXRPXAXKNIHEXTCYXTYHGCFHSYXK
sage: A.deciphering(a, b, C)
THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
sage: A.deciphering(a, b, C) == P
True
We can also use functional notation to work through the previous example:
sage: A = AffineCryptosystem(AlphabeticStrings()); A
Affine cryptosystem on Free alphabetic string monoid on A-Z
sage: P = A.encoding("The affine cryptosystem generalizes the shift cipher.")
sage: P
THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
sage: a, b = (9, 13)
sage: E = A(a, b); E
Affine cipher on Free alphabetic string monoid on A-Z
sage: C = E(P); C
CYXNGGHAXFKVSCJTVTCXRPXAXKNIHEXTCYXTYHGCFHSYXK
sage: aInv, bInv = A.inverse_key(a, b)
sage: D = A(aInv, bInv); D
Affine cipher on Free alphabetic string monoid on A-Z
sage: D(C)
THEAFFINECRYPTOSYSTEMGENERALIZESTHESHIFTCIPHER
sage: D(C) == P
True
sage: D(C) == P == D(E(P))
True
Encrypting the ciphertext with the inverse key also produces the plaintext:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: P = A.encoding("Encrypt with inverse key.")
sage: a, b = (11, 8)
sage: C = A.enciphering(a, b, P)
sage: P; C
ENCRYPTWITHINVERSEKEY
AVENMRJQSJHSVFANYAOAM
sage: aInv, bInv = A.inverse_key(a, b)
(continues on next page)
For a secret key (𝑎, 𝑏) ∈ Z/𝑛Z×Z/𝑛Z, if 𝑎 = 1 then any affine cryptosystem with key (1, 𝑏) for any 𝑏 ∈ Z/𝑛Z
is a shift cryptosystem. Here is how we can create a Caesar cipher using an affine cipher:
Any affine cipher with keys of the form (𝑎, 0) ∈ Z/𝑛Z × Z/𝑛Z is called a decimation cipher on the Roman
alphabet, or decimation cipher for short:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: P = A.encoding("A decimation cipher is a specialized affine cipher.")
sage: a, b = (17, 0)
sage: C = A.enciphering(a, b, P)
sage: P; C
ADECIMATIONCIPHERISASPECIALIZEDAFFINECIPHER
AZQIGWALGENIGVPQDGUAUVQIGAFGJQZAHHGNQIGVPQD
sage: A.deciphering(a, b, C) == P
True
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: P = A.encoding("An affine cipher with a random key.")
sage: a, b = A.random_key()
sage: C = A.enciphering(a, b, P)
sage: A.deciphering(a, b, C) == P
True
REFERENCES:
• [Sti2006]
brute_force(C, ranking=’none’)
Attempt a brute force cryptanalysis of the ciphertext C.
INPUT:
• C – A ciphertext over one of the supported alphabets of this affine cryptosystem. See the class
AffineCryptosystem for documentation on the supported alphabets.
• ranking – (default "none") the method to use for ranking all possible keys. If
ranking="none", then do not use any ranking function. The following ranking functions are
supported:
– "chi_square" – the chi-square ranking function as implemented in the method
rank_by_chi_square().
9
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (3, 7)
sage: P = A.encoding("Linear"); P
LINEAR
sage: C = A.enciphering(a, b, P)
sage: L = A.brute_force(C)
sage: sorted(L.items())[:26] # display 26 candidate decipherments
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (3, 7)
sage: P = A.encoding("Linear functions for encrypting and decrypting."); P
LINEARFUNCTIONSFORENCRYPTINGANDDECRYPTING
sage: C = A.enciphering(a, b, P)
sage: Rank = A.brute_force(C, ranking="chisquare")
sage: Rank[:10] # display only the top 10 candidate keys
deciphering(a, b, C)
Decrypt the ciphertext C with the key (a, b) using affine cipher decryption.
INPUT:
• a, b – a secret key belonging to the key space of this affine cipher. This key must be an element of
Z/𝑛Z × Z/𝑛Z such that gcd(𝑎, 𝑛) = 1 with 𝑛 being the size of the ciphertext and plaintext spaces.
• C – a string of ciphertext; possibly an empty string. Characters in this string must be encoded using
one of the supported alphabets. See the method encoding() for more information.
OUTPUT:
• The plaintext corresponding to the ciphertext C.
EXAMPLES:
Decryption over the capital letters of the English alphabet:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (5, 2)
sage: P = A.encoding("Affine functions are linear functions.")
sage: C = A.enciphering(a, b, P); C
CBBQPWBYPMTQUPOCJWFQPWCJBYPMTQUPO
sage: P == A.deciphering(a, b, C)
True
The previous example can also be worked through using functional notation:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (5, 2)
sage: P = A.encoding("Affine functions are linear functions.")
sage: E = A(a, b); E
(continues on next page)
11
Sage 9.1 Reference Manual: Cryptography, Release 9.1
If the ciphertext is an empty string, then the plaintext is also an empty string regardless of the value of the
secret key:
sage: a, b = A.random_key()
sage: A.deciphering(a, b, A.encoding(""))
enciphering(a, b, P)
Encrypt the plaintext P with the key (a, b) using affine cipher encryption.
INPUT:
• a, b – a secret key belonging to the key space of this affine cipher. This key must be an element of
Z/𝑛Z × Z/𝑛Z such that gcd(𝑎, 𝑛) = 1 with 𝑛 being the size of the ciphertext and plaintext spaces.
• P – a string of plaintext; possibly an empty string. Characters in this string must be encoded using
one of the supported alphabets. See the method encoding() for more information.
OUTPUT:
• The ciphertext corresponding to the plaintext P.
EXAMPLES:
Encryption over the capital letters of the English alphabet:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (3, 6)
sage: P = A.encoding("Affine ciphers work with linear functions.")
sage: A.enciphering(a, b, P)
GVVETSMEZBSFIUWFKUELBNETSGFVOTMLEWTI
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (3, 6)
sage: P = A.encoding("Affine ciphers work with linear functions.")
sage: E = A(a, b); E
Affine cipher on Free alphabetic string monoid on A-Z
sage: E(P)
GVVETSMEZBSFIUWFKUELBNETSGFVOTMLEWTI
If the plaintext is an empty string, then the ciphertext is also an empty string regardless of the value of the
secret key:
sage: a, b = A.random_key()
sage: A.enciphering(a, b, A.encoding(""))
encoding(S)
The encoding of the string S over the string monoid of this affine cipher. For example, if the string monoid
of this cryptosystem is AlphabeticStringMonoid, then the encoding of S would be its upper-case
equivalent stripped of all non-alphabetic characters. Only the following alphabet is supported for the affine
cipher:
• capital letters of the English alphabet as implemented in AlphabeticStrings()
INPUT:
• S – a string, possibly empty.
OUTPUT:
• The encoding of S over the string monoid of this cryptosystem. If S is an empty string, return an
empty string.
EXAMPLES:
Encoding over the upper-case letters of the English alphabet:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: A.encoding("Affine cipher over capital letters of the English alphabet.
˓→")
AFFINECIPHEROVERCAPITALLETTERSOFTHEENGLISHALPHABET
The argument S can be an empty string, in which case an empty string is returned:
sage: AffineCryptosystem(AlphabeticStrings()).encoding("")
inverse_key(a, b)
The inverse key corresponding to the secret key (𝑎, 𝑏). If 𝑝 is a plaintext character so that 𝑝 ∈ Z/𝑛Z and
𝑛 is the alphabet size, then the ciphertext 𝑐 corresponding to 𝑝 is
𝑐 ≡ 𝑎𝑝 + 𝑏 (mod 𝑛)
As (𝑎, 𝑏) is a key, then the multiplicative inverse 𝑎−1 exists and the original plaintext can be recovered as
follows
Therefore the ordered pair (𝑎−1 , −𝑏𝑎−1 ) is the inverse key corresponding to (𝑎, 𝑏).
INPUT:
• a, b – a secret key for this affine cipher. The ordered pair (𝑎, 𝑏) must be an element of Z/𝑛Z×Z/𝑛Z
such that gcd(𝑎, 𝑛) = 1.
OUTPUT:
• The inverse key (𝑎−1 , −𝑏𝑎−1 ) corresponding to (𝑎, 𝑏).
EXAMPLES:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (1, 2)
sage: A.inverse_key(a, b)
(1, 24)
sage: A.inverse_key(3, 2)
(9, 8)
13
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Suppose that the plaintext and ciphertext spaces are the capital letters of the English alphabet so that
𝑛 = 26. If 𝜙(𝑛) is the Euler phi function of 𝑛, then there are 𝜙(𝑛) integers 0 ≤ 𝑎 < 𝑛 that are relatively
prime to 𝑛. For the capital letters of the English alphabet, there are 12 such integers relatively prime to 𝑛:
sage: euler_phi(A.alphabet_size())
12
sage: n = A.alphabet_size()
sage: L = [i for i in range(n) if gcd(i, n) == 1]; L
[1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25]
Then a secret key (𝑎, 𝑏) of this shift cryptosystem is such that 𝑎 is an element of the list L in the last
example. Any inverse key (𝐴, 𝐵) corresponding to (𝑎, 𝑏) is such that 𝐴 is also in the list L above:
sage: a, b = (3, 9)
sage: a in L
True
sage: aInv, bInv = A.inverse_key(a, b)
sage: aInv, bInv
(9, 23)
sage: aInv in L
True
random_key()
Generate a random key within the key space of this affine cipher. The generated secret key is an ordered
pair (𝑎, 𝑏) ∈ Z/𝑛Z × Z/𝑛Z with 𝑛 being the size of the cipher domain and gcd(𝑎, 𝑛) = 1. Let 𝜙(𝑛)
denote the Euler phi function of 𝑛. Then the affine cipher has 𝑛 · 𝜙(𝑛) possible keys (see page 10 of
[Sti2006]).
OUTPUT:
• A random key within the key space of this affine cryptosystem. The output key is an ordered pair
(𝑎, 𝑏).
EXAMPLES:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: A.random_key() # random
(17, 25)
If (𝑎, 𝑏) is a secret key and 𝑛 is the size of the plaintext and ciphertext alphabets, then gcd(𝑎, 𝑛) = 1:
sage: a, b = A.random_key()
sage: n = A.alphabet_size()
sage: gcd(a, n)
1
rank_by_chi_square(C, pdict)
Use the chi-square statistic to rank all possible keys. Currently, this method only applies to the capital
letters of the English alphabet.
ALGORITHM:
Consider a non-empty alphabet 𝐴 consisting of 𝑛 elements, and let 𝐶 be a ciphertext encoded using
elements of 𝐴. The plaintext 𝑃 corresponding to 𝐶 is also encoded using elements of 𝐴. Let 𝑀 be a
candidate decipherment of 𝐶, i.e. 𝑀 is the result of attempting to decrypt 𝐶 using a key (𝑎, 𝑏) which is not
necessarily the same key used to encrypt 𝑃 . Suppose 𝐹𝐴 (𝑒) is the characteristic frequency probability of
𝑒 ∈ 𝐴 and let 𝐹𝑀 (𝑒) be the message frequency probability with respect to 𝑀 . The characteristic frequency
probability distribution of an alphabet is the expected frequency probability distribution for that alphabet.
The message frequency probability distribution of 𝑀 provides a distribution of the ratio of character
occurrences over message length. One can interpret the characteristic frequency probability 𝐹𝐴 (𝑒) as
the expected probability, while the message frequency probability 𝐹𝑀 (𝑒) is the observed probability. If
𝑀 is of length 𝐿, then the observed frequency of 𝑒 ∈ 𝐴 is
𝑂𝑀 (𝑒) = 𝐹𝑀 (𝑒) · 𝐿
𝐸𝐴 (𝑒) = 𝐹𝐴 (𝑒) · 𝐿
The chi-square rank 𝑅𝜒2 (𝑀 ) of 𝑀 corresponding to a key (𝑎, 𝑏) ∈ Z/𝑛Z × Z/𝑛Z is given by
∑︁ 𝑂𝑀 (𝑒) − 𝐸𝐴 (𝑒) 2
(︀ )︀
𝑅𝜒2 (𝑀 ) =
𝐸𝐴 (𝑒)
𝑒∈𝐴
Cryptanalysis by exhaustive
{︀ key search produces a }︀candidate decipherment 𝑀𝑎,𝑏 for each possible key
(𝑎, 𝑏). For a set 𝐷 = 𝑀𝑎1 ,𝑏1 , 𝑀𝑎2 ,𝑏2 , . . . , 𝑀𝑎𝑘 ,𝑏𝑘 of all candidate decipherments corresponding to a
ciphertext 𝐶, the smaller is the rank 𝑅𝜒2 (𝑀𝑎𝑖 ,𝑏𝑖 ) the more likely that (𝑎𝑖 , 𝑏𝑖 ) is the secret key. This key
ranking method is based on the Pearson chi-square test [PearsonTest].
INPUT:
• C – The ciphertext, a non-empty string. The ciphertext must be encoded using the upper-case letters
of the English alphabet.
• pdict – A dictionary of key, possible plaintext pairs. This should be the output of
brute_force() with ranking="none".
OUTPUT:
• A list ranking the most likely keys first. Each element of the list is a tuple of key, possible plaintext
pairs.
EXAMPLES:
Use the chi-square statistic to rank all possible keys and their corresponding decipherment:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (3, 7)
sage: P = A.encoding("Line.")
sage: C = A.enciphering(a, b, P)
sage: Plist = A.brute_force(C)
sage: Rank = A.rank_by_chi_square(C, Plist)
sage: Rank[:10] # display only the top 10 candidate keys
As more ciphertext is available, the reliability of the chi-square ranking function increases:
15
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (11, 24)
sage: P = A.encoding("Longer message is more information for cryptanalysis.")
sage: C = A.enciphering(a, b, P)
sage: Plist = A.brute_force(C)
sage: Rank = A.rank_by_chi_square(C, Plist)
sage: Rank[:10] # display only the top 10 candidate keys
rank_by_squared_differences(C, pdict)
Use the squared-differences measure to rank all possible keys. Currently, this method only applies to the
capital letters of the English alphabet.
ALGORITHM:
Consider a non-empty alphabet 𝐴 consisting of 𝑛 elements, and let 𝐶 be a ciphertext encoded using
elements of 𝐴. The plaintext 𝑃 corresponding to 𝐶 is also encoded using elements of 𝐴. Let 𝑀 be a
candidate decipherment of 𝐶, i.e. 𝑀 is the result of attempting to decrypt 𝐶 using a key (𝑎, 𝑏) which is not
necessarily the same key used to encrypt 𝑃 . Suppose 𝐹𝐴 (𝑒) is the characteristic frequency probability of
𝑒 ∈ 𝐴 and let 𝐹𝑀 (𝑒) be the message frequency probability with respect to 𝑀 . The characteristic frequency
probability distribution of an alphabet is the expected frequency probability distribution for that alphabet.
The message frequency probability distribution of 𝑀 provides a distribution of the ratio of character
occurrences over message length. One can interpret the characteristic frequency probability 𝐹𝐴 (𝑒) as
the expected probability, while the message frequency probability 𝐹𝑀 (𝑒) is the observed probability. If
𝑀 is of length 𝐿, then the observed frequency of 𝑒 ∈ 𝐴 is
𝑂𝑀 (𝑒) = 𝐹𝑀 (𝑒) · 𝐿
𝐸𝐴 (𝑒) = 𝐹𝐴 (𝑒) · 𝐿
The squared-differences, or residual sum of squares, rank 𝑅𝑅𝑆𝑆 (𝑀 ) of 𝑀 corresponding to a key (𝑎, 𝑏) ∈
Z/𝑛Z × Z/𝑛Z is given by
∑︁ (︀ )︀2
𝑅𝑅𝑆𝑆 (𝑀 ) = 𝑂𝑀 (𝑒) − 𝐸𝐴 (𝑒)
𝑒∈𝐴
Cryptanalysis by exhaustive
{︀ key search produces a }︀candidate decipherment 𝑀𝑎,𝑏 for each possible key
(𝑎, 𝑏). For a set 𝐷 = 𝑀𝑎1 ,𝑏1 , 𝑀𝑎2 ,𝑏2 , . . . , 𝑀𝑎𝑘 ,𝑏𝑘 of all candidate decipherments corresponding to a
ciphertext 𝐶, the smaller is the rank 𝑅𝑅𝑆𝑆 (𝑀𝑎𝑖 ,𝑏𝑖 ) the more likely that (𝑎𝑖 , 𝑏𝑖 ) is the secret key. This key
ranking method is based on the residual sum of squares measure [RSS].
INPUT:
• C – The ciphertext, a non-empty string. The ciphertext must be encoded using the upper-case letters
of the English alphabet.
• pdict – A dictionary of key, possible plaintext pairs. This should be the output of
brute_force() with ranking="none".
OUTPUT:
• A list ranking the most likely keys first. Each element of the list is a tuple of key, possible plaintext
pairs.
EXAMPLES:
Use the method of squared differences to rank all possible keys and their corresponding decipherment:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (3, 7)
sage: P = A.encoding("Line.")
sage: C = A.enciphering(a, b, P)
sage: Plist = A.brute_force(C)
sage: Rank = A.rank_by_squared_differences(C, Plist)
sage: Rank[:10] # display only the top 10 candidate keys
As more ciphertext is available, the reliability of the squared-differences ranking function increases:
sage: A = AffineCryptosystem(AlphabeticStrings())
sage: a, b = (11, 24)
sage: P = A.encoding("Longer message is more information for cryptanalysis.")
sage: C = A.enciphering(a, b, P)
sage: Plist = A.brute_force(C)
sage: Rank = A.rank_by_squared_differences(C, Plist)
sage: Rank[:10] # display only the top 10 candidate keys
class sage.crypto.classical.HillCryptosystem(S, m)
Bases: sage.crypto.cryptosystem.SymmetricKeyCryptosystem
Create a Hill cryptosystem defined by the 𝑚 x 𝑚 matrix space over Z/𝑁 Z, where 𝑁 is the alphabet size of the
string monoid S.
INPUT:
• S - a string monoid over some alphabet
17
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• m - integer > 0; the block length of matrices that specify block permutations
OUTPUT:
• A Hill cryptosystem of block length m over the alphabet S.
EXAMPLES:
sage: S = AlphabeticStrings()
sage: E = HillCryptosystem(S,3)
sage: E
Hill cryptosystem on Free alphabetic string monoid on A-Z of block length 3
sage: R = IntegerModRing(26)
sage: M = MatrixSpace(R,3,3)
sage: A = M([[1,0,1],[0,1,1],[2,2,3]])
sage: A
[1 0 1]
[0 1 1]
[2 2 3]
sage: e = E(A)
sage: e
Hill cipher on Free alphabetic string monoid on A-Z of block length 3
sage: e(S("LAMAISONBLANCHE"))
JYVKSKQPELAYKPV
block_length()
The row or column dimension of a matrix specifying a block permutation. Encryption and decryption keys
of a Hill cipher are square matrices, i.e. the row and column dimensions of an encryption or decryption
key are the same. This row/column dimension is referred to as the block length.
OUTPUT:
• The block length of an encryption/decryption key.
EXAMPLES:
sage: A = AlphabeticStrings()
sage: n = randint(1, A.ngens() - 1)
sage: H = HillCryptosystem(A, n)
sage: H.block_length() == n
True
deciphering(A, C)
Decrypt the ciphertext C using the key A.
INPUT:
• A - a key within the key space of this Hill cipher
• C - a string (possibly empty) over the string monoid of this Hill cipher
OUTPUT:
• The plaintext corresponding to the ciphertext C.
EXAMPLES:
sage: H = HillCryptosystem(AlphabeticStrings(), 3)
sage: K = H.random_key()
sage: M = H.encoding("Good day, mate! How ya going?")
sage: H.deciphering(K, H.enciphering(K, M)) == M
True
enciphering(A, M)
Encrypt the plaintext M using the key A.
INPUT:
• A - a key within the key space of this Hill cipher
• M - a string (possibly empty) over the string monoid of this Hill cipher.
OUTPUT:
• The ciphertext corresponding to the plaintext M.
EXAMPLES:
sage: H = HillCryptosystem(AlphabeticStrings(), 3)
sage: K = H.random_key()
sage: M = H.encoding("Good day, mate! How ya going?")
sage: H.deciphering(K, H.enciphering(K, M)) == M
True
encoding(M)
The encoding of the string M over the string monoid of this Hill cipher. For example, if the string monoid
of this Hill cipher is AlphabeticStringMonoid, then the encoding of M would be its upper-case
equivalent stripped of all non-alphabetic characters.
INPUT:
• M - a string, possibly empty
OUTPUT:
• The encoding of M over the string monoid of this Hill cipher.
EXAMPLES:
inverse_key(A)
The inverse key corresponding to the key A.
INPUT:
• A - an invertible matrix of the key space of this Hill cipher
OUTPUT:
• The inverse matrix of A.
EXAMPLES:
sage: S = AlphabeticStrings()
sage: E = HillCryptosystem(S,3)
sage: A = E.random_key()
sage: B = E.inverse_key(A)
sage: M = S("LAMAISONBLANCHE")
sage: e = E(A)
sage: c = E(B)
sage: c(e(M))
LAMAISONBLANCHE
19
Sage 9.1 Reference Manual: Cryptography, Release 9.1
random_key()
A random key within the key space of this Hill cipher. That is, generate a random 𝑚 x 𝑚 matrix to be used
as a block permutation, where 𝑚 is the block length of this Hill cipher. If 𝑛 is the size of the cryptosystem
2
alphabet, then there are 𝑛𝑚 possible keys. However the number of valid keys, i.e. invertible 𝑚 x 𝑚 square
2
matrices, is smaller than 𝑛𝑚 .
OUTPUT:
• A random key within the key space of this Hill cipher.
EXAMPLES:
sage: A = AlphabeticStrings()
sage: n = 3
sage: H = HillCryptosystem(A, n)
sage: K = H.random_key()
sage: Ki = H.inverse_key(K)
sage: M = "LAMAISONBLANCHE"
sage: e = H(K)
sage: d = H(Ki)
sage: d(e(A(M))) == A(M)
True
class sage.crypto.classical.ShiftCryptosystem(A)
Bases: sage.crypto.cryptosystem.SymmetricKeyCryptosystem
Create a shift cryptosystem.
Let 𝐴 = {𝑎0 , 𝑎1 , 𝑎2 , . . . , 𝑎𝑛−1 } be a non-empty alphabet consisting of 𝑛 unique elements. Define a mapping
𝑓 : 𝐴 −→ Z/𝑛Z from the alphabet 𝐴 to the set Z/𝑛Z of integers modulo 𝑛, given by 𝑓 (𝑎𝑖 ) = 𝑖. Thus we
can identify each element of the alphabet 𝐴 with a unique integer 0 ≤ 𝑖 < 𝑛. A key of the shift cipher is an
integer 0 ≤ 𝑘 < 𝑛. Therefore the key space is Z/𝑛Z. Since we assume that 𝐴 does not have repeated elements,
the mapping 𝑓 : 𝐴 −→ Z/𝑛Z is bijective. Encryption works by moving along the alphabet by 𝑘 positions,
with wrap around. Decryption reverses the process by moving backwards by 𝑘 positions, with wrap around.
More generally, let 𝑘 be a secret key, i.e. an element of the key space, and let 𝑝 be a plaintext character and
consequently 𝑝 ∈ Z/𝑛Z. Then the ciphertext character 𝑐 corresponding to 𝑝 is given by
𝑐≡𝑝+𝑘 (mod 𝑛)
Similarly, given a ciphertext character 𝑐 ∈ Z/𝑛Z and a secret key 𝑘, we can recover the corresponding plaintext
character as follows:
𝑝≡𝑐−𝑘 (mod 𝑛)
Use the bijection 𝑓 : 𝐴 −→ Z/𝑛Z to convert 𝑐 and 𝑝 back to elements of the alphabet 𝐴. Currently, the
following alphabets are supported for the shift cipher:
• capital letters of the English alphabet as implemented in AlphabeticStrings()
• the alphabet consisting of the hexadecimal number system as implemented in
HexadecimalStrings()
• the alphabet consisting of the binary number system as implemented in BinaryStrings()
EXAMPLES:
Some examples illustrating encryption and decryption over various alphabets. Here is an example over the
upper-case letters of the English alphabet:
sage: S = ShiftCryptosystem(AlphabeticStrings()); S
Shift cryptosystem on Free alphabetic string monoid on A-Z
sage: P = S.encoding("The shift cryptosystem generalizes the Caesar cipher.")
sage: P
THESHIFTCRYPTOSYSTEMGENERALIZESTHECAESARCIPHER
sage: K = 7
sage: C = S.enciphering(K, P); C
AOLZOPMAJYFWAVZFZALTNLULYHSPGLZAOLJHLZHYJPWOLY
sage: S.deciphering(K, C)
THESHIFTCRYPTOSYSTEMGENERALIZESTHECAESARCIPHER
sage: S.deciphering(K, C) == P
True
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: P = S.encoding("The shift cryptosystem generalizes the Caesar cipher.")
sage: K = 7
sage: E = S(K); E
Shift cipher on Free alphabetic string monoid on A-Z
sage: C = E(P); C
AOLZOPMAJYFWAVZFZALTNLULYHSPGLZAOLJHLZHYJPWOLY
sage: D = S(S.inverse_key(K)); D
Shift cipher on Free alphabetic string monoid on A-Z
sage: D(C) == P
True
sage: D(C) == P == D(E(P))
True
sage: S = ShiftCryptosystem(HexadecimalStrings()); S
Shift cryptosystem on Free hexadecimal string monoid
sage: P = S.encoding("Encryption & decryption shifts along the alphabet."); P
456e6372797074696f6e20262064656372797074696f6e2073686966747320616c6f6e672074686520616c7068616265
sage: K = 5
sage: C = S.enciphering(K, P); C
9ab3b8c7cec5c9beb4b3757b75b9bab8c7cec5c9beb4b375c8bdbebbc9c875b6b1b4b3bc75c9bdba75b6b1c5bdb6b7ba
sage: S.deciphering(K, C)
456e6372797074696f6e20262064656372797074696f6e2073686966747320616c6f6e672074686520616c7068616265
sage: S.deciphering(K, C) == P
True
sage: S = ShiftCryptosystem(BinaryStrings()); S
Shift cryptosystem on Free binary string monoid
sage: P = S.encoding("The binary alphabet is very insecure."); P
010101000110100001100101001000000110001001101001011011100110000101110010011110010010000001100001
sage: K = 1
sage: C = S.enciphering(K, P); C
101010111001011110011010110111111001110110010110100100011001111010001101100001101101111110011110
sage: S.deciphering(K, C)
010101000110100001100101001000000110001001101001011011100110000101110010011110010010000001100001
sage: S.deciphering(K, C) == P
True
A shift cryptosystem with key 𝑘 = 3 is commonly referred to as the Caesar cipher. Create a Caesar cipher over
21
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: P = S.encoding("Shift cipher with a random key.")
sage: K = S.random_key()
sage: C = S.enciphering(K, P)
sage: S.deciphering(K, C) == P
True
Decrypting with the key K is equivalent to encrypting with its corresponding inverse key:
sage: S.enciphering(S.inverse_key(K), C) == P
True
brute_force(C, ranking=’none’)
Attempt a brute force cryptanalysis of the ciphertext C.
INPUT:
• C – A ciphertext over one of the supported alphabets of this shift cryptosystem. See the class
ShiftCryptosystem for documentation on the supported alphabets.
• ranking – (default "none") the method to use for ranking all possible keys. If
ranking="none", then do not use any ranking function. The following ranking functions are
supported:
– "chisquare" – the chi-square ranking function as implemented in the method
rank_by_chi_square().
– "squared_differences" – the squared differences ranking function as implemented in the
method rank_by_squared_differences().
OUTPUT:
• All the possible plaintext sequences corresponding to the ciphertext C. This method effectively uses all
the possible keys in this shift cryptosystem to decrypt C. The method is also referred to as exhaustive
key search. The output is a dictionary of key, plaintext pairs.
EXAMPLES:
Cryptanalyze using all possible keys for various alphabets. Over the upper-case letters of the English
alphabet:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: P = S.encoding("The shift cryptosystem generalizes the Caesar cipher.")
sage: K = 7
sage: C = S.enciphering(K, P)
sage: Dict = S.brute_force(C)
sage: for k in range(len(Dict)):
(continues on next page)
[(0, APQNBQVOCAQVOUWLCTIZIZQBPUMBQK),
(1, ZOPMAPUNBZPUNTVKBSHYHYPAOTLAPJ),
(2, YNOLZOTMAYOTMSUJARGXGXOZNSKZOI),
(3, XMNKYNSLZXNSLRTIZQFWFWNYMRJYNH),
(4, WLMJXMRKYWMRKQSHYPEVEVMXLQIXMG),
(5, VKLIWLQJXVLQJPRGXODUDULWKPHWLF),
(6, UJKHVKPIWUKPIOQFWNCTCTKVJOGVKE),
(7, TIJGUJOHVTJOHNPEVMBSBSJUINFUJD),
(8, SHIFTINGUSINGMODULARARITHMETIC),
(9, RGHESHMFTRHMFLNCTKZQZQHSGLDSHB),
(10, QFGDRGLESQGLEKMBSJYPYPGRFKCRGA),
(11, PEFCQFKDRPFKDJLARIXOXOFQEJBQFZ),
(12, ODEBPEJCQOEJCIKZQHWNWNEPDIAPEY),
(13, NCDAODIBPNDIBHJYPGVMVMDOCHZODX),
(14, MBCZNCHAOMCHAGIXOFULULCNBGYNCW),
(15, LABYMBGZNLBGZFHWNETKTKBMAFXMBV),
(16, KZAXLAFYMKAFYEGVMDSJSJALZEWLAU),
(17, JYZWKZEXLJZEXDFULCRIRIZKYDVKZT),
(18, IXYVJYDWKIYDWCETKBQHQHYJXCUJYS),
(19, HWXUIXCVJHXCVBDSJAPGPGXIWBTIXR),
(continues on next page)
23
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[(8, SHIFTINGUSINGMODULARARITHMETIC),
(14, MBCZNCHAOMCHAGIXOFULULCNBGYNCW),
(20, GVWTHWBUIGWBUACRIZOFOFWHVASHWQ),
(13, NCDAODIBPNDIBHJYPGVMVMDOCHZODX),
(1, ZOPMAPUNBZPUNTVKBSHYHYPAOTLAPJ),
(23, DSTQETYRFDTYRXZOFWLCLCTESXPETN),
(10, QFGDRGLESQGLEKMBSJYPYPGRFKCRGA),
(6, UJKHVKPIWUKPIOQFWNCTCTKVJOGVKE),
(22, ETURFUZSGEUZSYAPGXMDMDUFTYQFUO),
(15, LABYMBGZNLBGZFHWNETKTKBMAFXMBV),
(12, ODEBPEJCQOEJCIKZQHWNWNEPDIAPEY),
(21, FUVSGVATHFVATZBQHYNENEVGUZRGVP),
(16, KZAXLAFYMKAFYEGVMDSJSJALZEWLAU),
(25, BQROCRWPDBRWPVXMDUJAJARCQVNCRL),
(9, RGHESHMFTRHMFLNCTKZQZQHSGLDSHB),
(24, CRSPDSXQECSXQWYNEVKBKBSDRWODSM),
(3, XMNKYNSLZXNSLRTIZQFWFWNYMRJYNH),
(5, VKLIWLQJXVLQJPRGXODUDULWKPHWLF),
(7, TIJGUJOHVTJOHNPEVMBSBSJUINFUJD),
(2, YNOLZOTMAYOTMSUJARGXGXOZNSKZOI),
(18, IXYVJYDWKIYDWCETKBQHQHYJXCUJYS),
(4, WLMJXMRKYWMRKQSHYPEVEVMXLQIXMG),
(11, PEFCQFKDRPFKDJLARIXOXOFQEJBQFZ),
(19, HWXUIXCVJHXCVBDSJAPGPGXIWBTIXR),
(0, APQNBQVOCAQVOUWLCTIZIZQBPUMBQK),
(17, JYZWKZEXLJZEXDFULCRIRIZKYDVKZT)]
[(8, SHIFTINGUSINGMODULARARITHMETIC),
(23, DSTQETYRFDTYRXZOFWLCLCTESXPETN),
(12, ODEBPEJCQOEJCIKZQHWNWNEPDIAPEY),
(2, YNOLZOTMAYOTMSUJARGXGXOZNSKZOI),
(9, RGHESHMFTRHMFLNCTKZQZQHSGLDSHB),
(7, TIJGUJOHVTJOHNPEVMBSBSJUINFUJD),
(21, FUVSGVATHFVATZBQHYNENEVGUZRGVP),
(22, ETURFUZSGEUZSYAPGXMDMDUFTYQFUO),
(1, ZOPMAPUNBZPUNTVKBSHYHYPAOTLAPJ),
(16, KZAXLAFYMKAFYEGVMDSJSJALZEWLAU),
(20, GVWTHWBUIGWBUACRIZOFOFWHVASHWQ),
(24, CRSPDSXQECSXQWYNEVKBKBSDRWODSM),
(14, MBCZNCHAOMCHAGIXOFULULCNBGYNCW),
(13, NCDAODIBPNDIBHJYPGVMVMDOCHZODX),
(3, XMNKYNSLZXNSLRTIZQFWFWNYMRJYNH),
(continues on next page)
deciphering(K, C)
Decrypt the ciphertext C with the key K using shift cipher decryption.
INPUT:
• K – a secret key; a key belonging to the key space of this shift cipher. This key is an integer 𝑘 satisfying
the inequality 0 ≤ 𝑘 < 𝑛, where 𝑛 is the size of the cipher domain.
• C – a string of ciphertext; possibly an empty string. Characters in this string must be encoded using
one of the supported alphabets. See the method encoding() for more information.
OUTPUT:
• The plaintext corresponding to the ciphertext C.
EXAMPLES:
Let’s perform decryption over the supported alphabets. Here is decryption over the capital letters of the
English alphabet:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: P = S.encoding("Stop shifting me."); P
STOPSHIFTINGME
sage: K = 13
sage: C = S.enciphering(K, P); C
FGBCFUVSGVATZR
sage: S.deciphering(K, C) == P
True
25
Sage 9.1 Reference Manual: Cryptography, Release 9.1
enciphering(K, P)
Encrypt the plaintext P with the key K using shift cipher encryption.
INPUT:
• K – a key belonging to the key space of this shift cipher. This key is an integer 𝑘 satisfying the
inequality 0 ≤ 𝑘 < 𝑛, where 𝑛 is the size of the cipher domain.
• P – a string of plaintext; possibly an empty string. Characters in this string must be encoded using
one of the supported alphabets. See the method encoding() for more information.
OUTPUT:
• The ciphertext corresponding to the plaintext P.
EXAMPLES:
Let’s perform encryption over the supported alphabets. Here is encryption over the capital letters of the
English alphabet:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: P = S.encoding("Shift your gear."); P
SHIFTYOURGEAR
sage: K = 3
sage: S.enciphering(K, P)
VKLIWBRXUJHDU
sage: S = ShiftCryptosystem(HexadecimalStrings())
sage: P = S.encoding("Capitalize with the shift key."); P
4361706974616c697a65207769746820746865207368696674206b65792e
sage: K = 5
sage: S.enciphering(K, P)
98b6c5bec9b6b1becfba75ccbec9bd75c9bdba75c8bdbebbc975b0bace73
sage: S = ShiftCryptosystem(BinaryStrings())
sage: P = S.encoding("Don't shift."); P
01000100011011110110111000100111011101000010000001110011011010000110100101100110011101000010
sage: K = 1
sage: S.enciphering(K, P)
10111011100100001001000111011000100010111101111110001100100101111001011010011001100010111101
encoding(S)
The encoding of the string S over the string monoid of this shift cipher. For example, if the string monoid
of this cryptosystem is AlphabeticStringMonoid, then the encoding of S would be its upper-case
equivalent stripped of all non-alphabetic characters. The following alphabets are supported for the shift
cipher:
• capital letters of the English alphabet as implemented in AlphabeticStrings()
• the alphabet consisting of the hexadecimal number system as implemented in
HexadecimalStrings()
The argument S can be an empty string, in which case an empty string is returned:
sage: ShiftCryptosystem(AlphabeticStrings()).encoding("")
sage: ShiftCryptosystem(HexadecimalStrings()).encoding("")
sage: ShiftCryptosystem(BinaryStrings()).encoding("")
inverse_key(K)
The inverse key corresponding to the key K. For the shift cipher, the inverse key corresponding to K is
−𝐾 mod 𝑛, where 𝑛 > 0 is the size of the cipher domain, i.e. the plaintext/ciphertext space. A key 𝑘
of the shift cipher is an integer 0 ≤ 𝑘 < 𝑛. The key 𝑘 = 0 has no effect on either the plaintext or the
ciphertext.
INPUT:
• K – a key for this shift cipher. This must be an integer 𝑘 such that 0 ≤ 𝑘 < 𝑛, where 𝑛 is the size of
the cipher domain.
OUTPUT:
• The inverse key corresponding to K.
EXAMPLES:
Some random keys and their respective inverse keys:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: key = S.random_key(); key # random
2
sage: S.inverse_key(key) # random
(continues on next page)
27
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Regardless of the value of a key, the addition of the key and its inverse must be equal to the alphabet size.
This relationship holds exactly when the value of the key is non-zero:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: K = S.random_key()
sage: while K == 0:
....: K = S.random_key()
sage: invK = S.inverse_key(K)
sage: K + invK == S.alphabet_size()
True
sage: invK + K == S.alphabet_size()
True
sage: K = S.random_key()
sage: while K != 0:
....: K = S.random_key()
sage: invK = S.inverse_key(K)
sage: K + invK != S.alphabet_size()
True
sage: K; invK
0
0
random_key()
Generate a random key within the key space of this shift cipher. The generated key is an integer 0 ≤ 𝑘 < 𝑛
with 𝑛 being the size of the cipher domain. Thus there are 𝑛 possible keys in the key space, which is the
set Z/𝑛Z. The key 𝑘 = 0 has no effect on either the plaintext or the ciphertext.
OUTPUT:
• A random key within the key space of this shift cryptosystem.
EXAMPLES:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: S.random_key() # random
18
sage: S = ShiftCryptosystem(BinaryStrings())
sage: S.random_key() # random
0
sage: S = ShiftCryptosystem(HexadecimalStrings())
sage: S.random_key() # random
5
Regardless of the value of a key, the addition of the key and its inverse must be equal to the alphabet size.
This relationship holds exactly when the value of the key is non-zero:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: K = S.random_key()
sage: while K == 0:
....: K = S.random_key()
sage: invK = S.inverse_key(K)
sage: K + invK == S.alphabet_size()
True
sage: invK + K == S.alphabet_size()
True
sage: K = S.random_key()
sage: while K != 0:
....: K = S.random_key()
sage: invK = S.inverse_key(K)
sage: K + invK != S.alphabet_size()
True
sage: K; invK
0
0
rank_by_chi_square(C, pdict)
Use the chi-square statistic to rank all possible keys. Currently, this method only applies to the capital
letters of the English alphabet.
ALGORITHM:
Consider a non-empty alphabet 𝐴 consisting of 𝑛 elements, and let 𝐶 be a ciphertext encoded using el-
ements of 𝐴. The plaintext 𝑃 corresponding to 𝐶 is also encoded using elements of 𝐴. Let 𝑀 be a
candidate decipherment of 𝐶, i.e. 𝑀 is the result of attempting to decrypt 𝐶 using a key 𝑘 ∈ Z/𝑛Z which
is not necessarily the same key used to encrypt 𝑃 . Suppose 𝐹𝐴 (𝑒) is the characteristic frequency proba-
bility of 𝑒 ∈ 𝐴 and let 𝐹𝑀 (𝑒) be the message frequency probability with respect to 𝑀 . The characteristic
frequency probability distribution of an alphabet is the expected frequency probability distribution for that
alphabet. The message frequency probability distribution of 𝑀 provides a distribution of the ratio of char-
acter occurrences over message length. One can interpret the characteristic frequency probability 𝐹𝐴 (𝑒)
as the expected probability, while the message frequency probability 𝐹𝑀 (𝑒) is the observed probability. If
𝑀 is of length 𝐿, then the observed frequency of 𝑒 ∈ 𝐴 is
𝑂𝑀 (𝑒) = 𝐹𝑀 (𝑒) · 𝐿
𝐸𝐴 (𝑒) = 𝐹𝐴 (𝑒) · 𝐿
∑︁ 𝑂𝑀 (𝑒) − 𝐸𝐴 (𝑒) 2
(︀ )︀
𝑅𝜒2 (𝑀 ) =
𝐸𝐴 (𝑒)
𝑒∈𝐴
Cryptanalysis by exhaustive key{︀ search produces a}︀ candidate decipherment 𝑀𝑘 for each possible key
𝑘 ∈ Z/𝑛Z. For a set 𝐷 = 𝑀𝑘1 , 𝑀𝑘2 , . . . , 𝑀𝑘𝑟 of all candidate decipherments corresponding to a
ciphertext 𝐶, the smaller is the rank 𝑅𝜒2 (𝑀𝑘𝑖 ) the more likely that 𝑘𝑖 is the secret key. This key ranking
method is based on the Pearson chi-square test [PearsonTest].
INPUT:
• C – The ciphertext, a non-empty string. The ciphertext must be encoded using the upper-case letters
of the English alphabet.
29
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• pdict – A dictionary of key, possible plaintext pairs. This should be the output of
brute_force() with ranking="none".
OUTPUT:
• A list ranking the most likely keys first. Each element of the list is a tuple of key, possible plaintext
pairs.
EXAMPLES:
Use the chi-square statistic to rank all possible keys and their corresponding decipherment:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: P = S.encoding("Shi."); P
SHI
sage: K = 5
sage: C = S.enciphering(K, P)
sage: Pdict = S.brute_force(C)
sage: S.rank_by_chi_square(C, Pdict)
[(9, ODE),
(5, SHI),
(20, DST),
(19, ETU),
(21, CRS),
(10, NCD),
(25, YNO),
(6, RGH),
(12, LAB),
(8, PEF),
(1, WLM),
(11, MBC),
(18, FUV),
(17, GVW),
(2, VKL),
(4, TIJ),
(3, UJK),
(0, XMN),
(16, HWX),
(15, IXY),
(23, APQ),
(24, ZOP),
(22, BQR),
(7, QFG),
(13, KZA),
(14, JYZ)]
As more ciphertext is available, the reliability of the chi-square ranking function increases:
[(5, SHIFTCIPHER),
(9, ODEBPYELDAN),
(18, FUVSGPVCURE),
(2, VKLIWFLSKHU),
(continues on next page)
rank_by_squared_differences(C, pdict)
Use the squared-differences measure to rank all possible keys. Currently, this method only applies to the
capital letters of the English alphabet.
ALGORITHM:
Consider a non-empty alphabet 𝐴 consisting of 𝑛 elements, and let 𝐶 be a ciphertext encoded using el-
ements of 𝐴. The plaintext 𝑃 corresponding to 𝐶 is also encoded using elements of 𝐴. Let 𝑀 be a
candidate decipherment of 𝐶, i.e. 𝑀 is the result of attempting to decrypt 𝐶 using a key 𝑘 ∈ Z/𝑛Z which
is not necessarily the same key used to encrypt 𝑃 . Suppose 𝐹𝐴 (𝑒) is the characteristic frequency proba-
bility of 𝑒 ∈ 𝐴 and let 𝐹𝑀 (𝑒) be the message frequency probability with respect to 𝑀 . The characteristic
frequency probability distribution of an alphabet is the expected frequency probability distribution for that
alphabet. The message frequency probability distribution of 𝑀 provides a distribution of the ratio of char-
acter occurrences over message length. One can interpret the characteristic frequency probability 𝐹𝐴 (𝑒)
as the expected probability, while the message frequency probability 𝐹𝑀 (𝑒) is the observed probability. If
𝑀 is of length 𝐿, then the observed frequency of 𝑒 ∈ 𝐴 is
𝑂𝑀 (𝑒) = 𝐹𝑀 (𝑒) · 𝐿
𝐸𝐴 (𝑒) = 𝐹𝐴 (𝑒) · 𝐿
Cryptanalysis by exhaustive key {︀ search produces a}︀ candidate decipherment 𝑀𝑘 for each possible key
𝑘 ∈ Z/𝑛Z. For a set 𝐷 = 𝑀𝑘1 , 𝑀𝑘2 , . . . , 𝑀𝑘𝑟 of all candidate decipherments corresponding to a
ciphertext 𝐶, the smaller is the rank 𝑅𝑅𝑆𝑆 (𝑀𝑘𝑖 ) the more likely that 𝑘𝑖 is the secret key. This key ranking
method is based on the residual sum of squares measure [RSS].
INPUT:
31
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• C – The ciphertext, a non-empty string. The ciphertext must be encoded using the upper-case letters
of the English alphabet.
• pdict – A dictionary of key, possible plaintext pairs. This should be the output of
brute_force() with ranking="none".
OUTPUT:
• A list ranking the most likely keys first. Each element of the list is a tuple of key, possible plaintext
pairs.
EXAMPLES:
Use the method of squared differences to rank all possible keys and their corresponding decipherment:
sage: S = ShiftCryptosystem(AlphabeticStrings())
sage: P = S.encoding("Shi."); P
SHI
sage: K = 5
sage: C = S.enciphering(K, P)
sage: Pdict = S.brute_force(C)
sage: S.rank_by_squared_differences(C, Pdict)
[(19, ETU),
(9, ODE),
(20, DST),
(5, SHI),
(8, PEF),
(4, TIJ),
(25, YNO),
(21, CRS),
(6, RGH),
(10, NCD),
(12, LAB),
(23, APQ),
(24, ZOP),
(0, XMN),
(13, KZA),
(15, IXY),
(1, WLM),
(16, HWX),
(22, BQR),
(11, MBC),
(18, FUV),
(2, VKL),
(17, GVW),
(7, QFG),
(3, UJK),
(14, JYZ)]
As more ciphertext is available, the reliability of the squared differences ranking function increases:
sage: P = S.encoding("Shift cipher."); P
SHIFTCIPHER
sage: C = S.enciphering(K, P)
sage: Pdict = S.brute_force(C)
sage: S.rank_by_squared_differences(C, Pdict)
[(20, DSTQENTASPC),
(5, SHIFTCIPHER),
(continues on next page)
class sage.crypto.classical.SubstitutionCryptosystem(S)
Bases: sage.crypto.cryptosystem.SymmetricKeyCryptosystem
Create a substitution cryptosystem.
INPUT:
• S - a string monoid over some alphabet
OUTPUT:
• A substitution cryptosystem over the alphabet S.
EXAMPLES:
sage: M = AlphabeticStrings()
sage: E = SubstitutionCryptosystem(M)
sage: E
Substitution cryptosystem on Free alphabetic string monoid on A-Z
sage: K = M([ 25-i for i in range(26) ])
sage: K
ZYXWVUTSRQPONMLKJIHGFEDCBA
sage: e = E(K)
sage: m = M("THECATINTHEHAT")
sage: e(m)
GSVXZGRMGSVSZG
deciphering(K, C)
Decrypt the ciphertext C using the key K.
INPUT:
• K - a key belonging to the key space of this substitution cipher
• C - a string (possibly empty) over the string monoid of this cryptosystem.
33
Sage 9.1 Reference Manual: Cryptography, Release 9.1
OUTPUT:
• The plaintext corresponding to the ciphertext C.
EXAMPLES:
sage: S = SubstitutionCryptosystem(AlphabeticStrings())
sage: K = S.random_key()
sage: M = S.encoding("Don't substitute me!")
sage: S.deciphering(K, S.enciphering(K, M)) == M
True
enciphering(K, M)
Encrypt the plaintext M using the key K.
INPUT:
• K - a key belonging to the key space of this substitution cipher
• M - a string (possibly empty) over the string monoid of this cryptosystem.
OUTPUT:
• The ciphertext corresponding to the plaintext M.
EXAMPLES:
sage: S = SubstitutionCryptosystem(AlphabeticStrings())
sage: K = S.random_key()
sage: M = S.encoding("Don't substitute me.")
sage: S.deciphering(K, S.enciphering(K, M)) == M
True
encoding(M)
The encoding of the string M over the string monoid of this substitution cipher. For example, if the string
monoid of this cryptosystem is AlphabeticStringMonoid, then the encoding of M would be its
upper-case equivalent stripped of all non-alphabetic characters.
INPUT:
• M - a string, possibly empty
OUTPUT:
• The encoding of M over the string monoid of this cryptosystem.
EXAMPLES:
inverse_key(K)
The inverse key corresponding to the key K. The specified key is a permutation of the cryptosystem alpha-
bet.
INPUT:
• K - a key belonging to the key space of this cryptosystem
OUTPUT:
sage: S = AlphabeticStrings()
sage: E = SubstitutionCryptosystem(S)
sage: K = E.random_key()
sage: L = E.inverse_key(K)
sage: M = S("THECATINTHEHAT")
sage: e = E(K)
sage: c = E(L)
sage: c(e(M))
THECATINTHEHAT
random_key()
Generate a random key within the key space of this substitution cipher. The generated key is a permutation
of the cryptosystem alphabet. Let 𝑛 be the length of the alphabet. Then there are 𝑛! possible keys in the
key space.
OUTPUT:
• A random key within the key space of this cryptosystem.
EXAMPLES:
sage: A = AlphabeticStrings()
sage: S = SubstitutionCryptosystem(A)
sage: K = S.random_key()
sage: Ki = S.inverse_key(K)
sage: M = "THECATINTHEHAT"
sage: e = S(K)
sage: d = S(Ki)
sage: d(e(A(M))) == A(M)
True
class sage.crypto.classical.TranspositionCryptosystem(S, n)
Bases: sage.crypto.cryptosystem.SymmetricKeyCryptosystem
Create a transposition cryptosystem of block length n.
INPUT:
• S - a string monoid over some alphabet
• n - integer > 0; a block length of a block permutation
OUTPUT:
• A transposition cryptosystem of block length n over the alphabet S.
EXAMPLES:
sage: S = AlphabeticStrings()
sage: E = TranspositionCryptosystem(S,14)
sage: E
Transposition cryptosystem on Free alphabetic string monoid on A-Z of block
˓→length 14
35
Sage 9.1 Reference Manual: Cryptography, Release 9.1
deciphering(K, C)
Decrypt the ciphertext C using the key K.
INPUT:
• K - a key belonging to the key space of this transposition cipher
• C - a string (possibly empty) over the string monoid of this cryptosystem.
OUTPUT:
• The plaintext corresponding to the ciphertext C.
EXAMPLES:
enciphering(K, M)
Encrypt the plaintext M using the key K.
INPUT:
• K - a key belonging to the key space of this transposition cipher
• M - a string (possibly empty) over the string monoid of this cryptosystem
OUTPUT:
• The ciphertext corresponding to the plaintext M.
EXAMPLES:
encoding(M)
The encoding of the string M over the string monoid of this transposition cipher. For example, if the
string monoid of this cryptosystem is AlphabeticStringMonoid, then the encoding of M would be
its upper-case equivalent stripped of all non-alphabetic characters.
INPUT:
• M - a string, possibly empty
OUTPUT:
• The encoding of M over the string monoid of this cryptosystem.
EXAMPLES:
inverse_key(K, check=True)
The inverse key corresponding to the key K.
INPUT:
• K - a key belonging to the key space of this transposition cipher
• check - bool (default: True); check that K belongs to the key space of this cryptosystem.
OUTPUT:
• The inverse key corresponding to K.
EXAMPLES:
sage: S = AlphabeticStrings()
sage: E = TranspositionCryptosystem(S, 14)
sage: K = E.random_key()
sage: Ki = E.inverse_key(K)
sage: e = E(K)
sage: d = E(Ki)
sage: M = "THECATINTHEHAT"
sage: C = e(S(M))
sage: d(S(C)) == S(M)
True
random_key()
Generate a random key within the key space of this transposition cryptosystem. Let 𝑛 > 0 be the block
length of this cryptosystem. Then there are 𝑛! possible keys.
OUTPUT:
• A random key within the key space of this cryptosystem.
EXAMPLES:
sage: S = AlphabeticStrings()
sage: E = TranspositionCryptosystem(S, 14)
sage: K = E.random_key()
sage: Ki = E.inverse_key(K)
sage: e = E(K)
sage: d = E(Ki)
sage: M = "THECATINTHEHAT"
sage: C = e(S(M))
sage: d(S(C)) == S(M)
True
class sage.crypto.classical.VigenereCryptosystem(S, n)
Bases: sage.crypto.cryptosystem.SymmetricKeyCryptosystem
Create a Vigenere cryptosystem of block length n.
INPUT:
• S– a string monoid over some alphabet
37
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: S = AlphabeticStrings()
sage: E = VigenereCryptosystem(S,14)
sage: E
Vigenere cryptosystem on Free alphabetic string monoid on A-Z of period 14
sage: K = S('ABCDEFGHIJKLMN')
sage: K
ABCDEFGHIJKLMN
sage: e = E(K)
sage: e
Cipher on Free alphabetic string monoid on A-Z
sage: e(S("THECATINTHEHAT"))
TIGFEYOUBQOSMG
deciphering(K, C)
Decrypt the ciphertext C using the key K.
INPUT:
• K - a key belonging to the key space of this Vigenere cipher
• C - a string (possibly empty) over the string monoid of this cryptosystem
OUTPUT:
• The plaintext corresponding to the ciphertext C.
EXAMPLES:
enciphering(K, M)
Encrypt the plaintext M using the key K.
INPUT:
• K - a key belonging to the key space of this Vigenere cipher
• M - a string (possibly empty) over the string monoid of this cryptosystem
OUTPUT:
• The ciphertext corresponding to the plaintext M.
EXAMPLES:
encoding(M)
The encoding of the string M over the string monoid of this Vigenere cipher. For example, if the string
monoid of this cryptosystem is AlphabeticStringMonoid, then the encoding of M would be its
upper-case equivalent stripped of all non-alphabetic characters.
INPUT:
• M - a string, possibly empty
OUTPUT:
• The encoding of M over the string monoid of this cryptosystem.
EXAMPLES:
sage: A = AlphabeticStrings()
sage: V = VigenereCryptosystem(A, 24)
sage: M = "Jack and Jill went up the hill."
sage: V.encoding(M) == A.encoding(M)
True
inverse_key(K)
The inverse key corresponding to the key K.
INPUT:
• K - a key within the key space of this Vigenere cryptosystem
OUTPUT:
• The inverse key corresponding to K.
EXAMPLES:
sage: S = AlphabeticStrings()
sage: E = VigenereCryptosystem(S,14)
sage: K = E.random_key()
sage: L = E.inverse_key(K)
sage: M = S("THECATINTHEHAT")
sage: e = E(K)
sage: c = E(L)
sage: c(e(M))
THECATINTHEHAT
random_key()
Generate a random key within the key space of this Vigenere cryptosystem. Let 𝑛 > 0 be the length of the
cryptosystem alphabet and let 𝑚 > 0 be the block length of this cryptosystem. Then there are 𝑛𝑚 possible
keys.
OUTPUT:
• A random key within the key space of this cryptosystem.
EXAMPLES:
sage: A = AlphabeticStrings()
sage: V = VigenereCryptosystem(A, 14)
sage: M = "THECATINTHEHAT"
sage: K = V.random_key()
sage: Ki = V.inverse_key(K)
sage: e = V(K)
sage: d = V(Ki)
(continues on next page)
39
Sage 9.1 Reference Manual: Cryptography, Release 9.1
FOUR
CLASSICAL CIPHERS
41
Sage 9.1 Reference Manual: Cryptography, Release 9.1
FIVE
SIMPLIFIED DES
A simplified variant of the Data Encryption Standard (DES). Note that Simplified DES or S-DES is for educational
purposes only. It is a small-scale version of the DES designed to help beginners understand the basic structure of DES.
AUTHORS:
• Minh Van Nguyen (2009-06): initial version
class sage.crypto.block_cipher.sdes.SimplifiedDES
Bases: sage.structure.sage_object.SageObject
This class implements the Simplified Data Encryption Standard (S-DES) described in [Sch1996]. Schaefer’s
S-DES is for educational purposes only and is not secure for practical purposes. S-DES is a version of the DES
with all parameters significantly reduced, but at the same time preserving the structure of DES. The goal of
S-DES is to allow a beginner to understand the structure of DES, thus laying a foundation for a thorough study
of DES. Its goal is as a teaching tool in the same spirit as Phan’s Mini-AES [Pha2002].
EXAMPLES:
Encrypt a random block of 8-bit plaintext using a random key, decrypt the ciphertext, and compare the result
with the original plaintext:
We can also encrypt binary strings that are larger than 8 bits in length. However, the number of bits in that
binary string must be positive and a multiple of 8:
43
Sage 9.1 Reference Manual: Cryptography, Release 9.1
block_length()
Return the block length of Schaefer’s S-DES block cipher. A key in Schaefer’s S-DES is a block of 10
bits.
OUTPUT:
• The block (or key) length in number of bits.
EXAMPLES:
decrypt(C, K)
Return an 8-bit plaintext corresponding to the ciphertext C, using S-DES decryption with key K. The de-
cryption process of S-DES is as follows. Let 𝑃 be the initial permutation function, 𝑃 −1 the corresponding
inverse permutation, Π𝐹 the permutation/substitution function, and 𝜎 the switch function. The ciphertext
block C first goes through 𝑃 , the output of which goes through Π𝐹 using the second subkey. Then we
apply the switch function to the output of the last function, and the result is then fed into Π𝐹 using the first
subkey. Finally, run the output through 𝑃 −1 to get the plaintext.
INPUT:
• C – an 8-bit ciphertext; a block of 8 bits
• K – a 10-bit key; a block of 10 bits
OUTPUT:
The 8-bit plaintext corresponding to C, obtained using the key K.
EXAMPLES:
Decrypt an 8-bit ciphertext block:
sage: C = "01010101"
sage: K = "1010000010"
sage: sdes.decrypt(sdes.string_to_list(C), sdes.string_to_list(K))
[0, 0, 0, 1, 0, 1, 0, 1]
encrypt(P, K)
Return an 8-bit ciphertext corresponding to the plaintext P, using S-DES encryption with key K. The en-
cryption process of S-DES is as follows. Let 𝑃 be the initial permutation function, 𝑃 −1 the corresponding
inverse permutation, Π𝐹 the permutation/substitution function, and 𝜎 the switch function. The plaintext
block P first goes through 𝑃 , the output of which goes through Π𝐹 using the first subkey. Then we apply
the switch function to the output of the last function, and the result is then fed into Π𝐹 using the second
subkey. Finally, run the output through 𝑃 −1 to get the ciphertext.
INPUT:
• P – an 8-bit plaintext; a block of 8 bits
sage: P = "01010101"
sage: K = "1010000010"
sage: sdes.encrypt(sdes.string_to_list(P), sdes.string_to_list(K))
[1, 1, 0, 0, 0, 0, 0, 1]
initial_permutation(B, inverse=False)
Return the initial permutation of B. Denote the initial permutation function by 𝑃 and let (𝑏0 , 𝑏1 , 𝑏2 , . . . , 𝑏7 )
be a vector of 8 bits, where each 𝑏𝑖 ∈ {0, 1}. Then
𝑃 (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 ) = (𝑏1 , 𝑏5 , 𝑏2 , 𝑏0 , 𝑏3 , 𝑏7 , 𝑏4 , 𝑏6 )
𝑃 −1 (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 ) = (𝑏3 , 𝑏0 , 𝑏2 , 𝑏4 , 𝑏6 , 𝑏1 , 𝑏7 , 𝑏5 )
INPUT:
• B – list; a block of 8 bits
• inverse – (default: False) if True then use the inverse permutation 𝑃 −1 ; if False then use the
initial permutation 𝑃
OUTPUT:
The initial permutation of B if inverse=False, or the inverse permutation of B if inverse=True.
EXAMPLES:
The initial permutation of a list of 8 bits:
45
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: S = "10110100"
sage: L = sdes.string_to_list(S)
sage: P = sdes.initial_permutation(L); P
[0, 1, 1, 1, 1, 0, 0, 0]
sage: sdes.initial_permutation(sdes.string_to_list("01111000"), inverse=True)
[1, 0, 1, 1, 0, 1, 0, 0]
left_shift(B, n=1)
Return a circular left shift of B by n positions. Let 𝐵 = (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 , 𝑏8 , 𝑏9 ) be a vector of
10 bits. Then the left shift operation 𝐿𝑛 is performed on the first 5 bits and the last 5 bits of 𝐵 separately.
That is, if the number of shift positions is n=1, then 𝐿1 is defined as
𝐿1 (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 , 𝑏8 , 𝑏9 ) = (𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏0 , 𝑏6 , 𝑏7 , 𝑏8 , 𝑏9 , 𝑏5 )
𝐿2 (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 , 𝑏8 , 𝑏9 ) = (𝑏2 , 𝑏3 , 𝑏4 , 𝑏0 , 𝑏1 , 𝑏7 , 𝑏8 , 𝑏9 , 𝑏5 , 𝑏6 )
INPUT:
• B – a list of 10 bits
• n – (default: 1) if n=1 then perform left shift by 1 position; if n=2 then perform left shift by 2
positions. The valid values for n are 1 and 2, since only up to 2 positions are defined for this circular
left shift operation.
OUTPUT:
The circular left shift of each half of B.
EXAMPLES:
Circular left shift by 1 position of a 10-bit string:
sage: B = [0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
sage: sdes.left_shift(B, n=2)
[0, 0, 1, 0, 0, 0, 0, 0, 1, 1]
sage: S = "1000001100"
sage: L = sdes.string_to_list(S)
sage: sdes.left_shift(L)
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
sage: sdes.left_shift(sdes.string_to_list("1010000010"), n=2)
[1, 0, 0, 1, 0, 0, 1, 0, 0, 0]
list_to_string(B)
Return a binary string representation of the list B.
INPUT:
• B – a non-empty list of bits
OUTPUT:
The binary string representation of B.
EXAMPLES:
A binary string representation of a list of bits:
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
sage: sdes = SimplifiedDES()
sage: L = [0, 0, 0, 0, 1, 1, 0, 1, 0, 0]
sage: sdes.list_to_string(L)
0000110100
permutation10(B)
Return a permutation of a 10-bit string. This permutation is called 𝑃10 and is specified as follows. Let
(𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 , 𝑏8 , 𝑏9 ) be a vector of 10 bits where each 𝑏𝑖 ∈ {0, 1}. Then 𝑃10 is given by
INPUT:
• B – a block of 10-bit string
OUTPUT:
A permutation of B.
EXAMPLES:
Permute a 10-bit string:
sage: from sage.crypto.block_cipher.sdes import SimplifiedDES
sage: sdes = SimplifiedDES()
sage: B = [1, 1, 0, 0, 1, 0, 0, 1, 0, 1]
sage: sdes.permutation10(B)
[0, 1, 1, 0, 0, 1, 1, 0, 1, 0]
sage: sdes.permutation10([0, 1, 1, 0, 1, 0, 0, 1, 0, 1])
[1, 1, 1, 0, 0, 1, 0, 0, 1, 0]
sage: sdes.permutation10([1, 0, 1, 0, 0, 0, 0, 0, 1, 0])
[1, 0, 0, 0, 0, 0, 1, 1, 0, 0]
permutation4(B)
Return a permutation of a 4-bit string. This permutation is called 𝑃4 and is specified as follows. Let
(𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 ) be a vector of 4 bits where each 𝑏𝑖 ∈ {0, 1}. Then 𝑃4 is defined by
𝑃4 (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 ) = (𝑏1 , 𝑏3 , 𝑏2 , 𝑏0 )
47
Sage 9.1 Reference Manual: Cryptography, Release 9.1
INPUT:
• B – a block of 4-bit string
OUTPUT:
A permutation of B.
EXAMPLES:
Permute a 4-bit string:
sage: S = "1100"
sage: L = sdes.string_to_list(S)
sage: sdes.permutation4(L)
[1, 0, 0, 1]
sage: sdes.permutation4(sdes.string_to_list("0101"))
[1, 1, 0, 0]
permutation8(B)
Return a permutation of an 8-bit string. This permutation is called 𝑃8 and is specified as follows. Let
(𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 , 𝑏8 , 𝑏9 ) be a vector of 10 bits where each 𝑏𝑖 ∈ {0, 1}. Then 𝑃8 picks out 8 of
those 10 bits and permutes those 8 bits:
𝑃8 (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 , 𝑏8 , 𝑏9 ) = (𝑏5 , 𝑏2 , 𝑏6 , 𝑏3 , 𝑏7 , 𝑏4 , 𝑏9 , 𝑏8 )
INPUT:
• B – a block of 10-bit string
OUTPUT:
Pick out 8 of the 10 bits of B and permute those 8 bits.
EXAMPLES:
Permute a 10-bit string:
sage: S = "1100100101"
sage: L = sdes.string_to_list(S)
sage: sdes.permutation8(L)
[0, 0, 0, 0, 1, 1, 1, 0]
sage: sdes.permutation8(sdes.string_to_list("0110100101"))
[0, 1, 0, 0, 1, 1, 1, 0]
permute_substitute(B, key)
Apply the function Π𝐹 on the block B using subkey key. Let (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 ) be a vector of 8
bits where each 𝑏𝑖 ∈ {0, 1}, let 𝐿 and 𝑅 be the leftmost 4 bits and rightmost 4 bits of B respectively, and
let 𝐹 be a function mapping 4-bit strings to 4-bit strings. Then
Let 𝐾 = (𝑘0 , 𝑘1 , 𝑘2 , 𝑘3 , 𝑘4 , 𝑘5 , 𝑘6 , 𝑘7 ) be an 8-bit subkey. Then 𝐾 is added to the above expanded input
block using exclusive-OR to produce
𝑛3 + 𝑘0 𝑛0 + 𝑘1 𝑛1 + 𝑘2 𝑛2 + 𝑘3 𝑝 𝑝0,1 𝑝0,2 𝑝0,3
= 0,0
𝑛1 + 𝑘4 𝑛2 + 𝑘5 𝑛3 + 𝑘6 𝑛0 + 𝑘7 𝑝1,0 𝑝1,1 𝑝1,2 𝑝1,3
Now read the first row as the 4-bit string 𝑝0,0 𝑝0,3 𝑝0,1 𝑝0,2 and input this 4-bit string through S-box 𝑆0 to
get a 2-bit output.
Input Output Input Output
0000 01 1000 00
0001 00 1001 10
0010 11 1010 01
𝑆0 = 0011 10 1011 11
0100 11 1100 11
0101 10 1101 01
0110 01 1110 11
0111 00 1111 10
Next read the second row as the 4-bit string 𝑝1,0 𝑝1,3 𝑝1,1 𝑝1,2 and input this 4-bit string through S-box 𝑆1
to get another 2-bit output.
Input Output Input Output
0000 00 1000 11
0001 01 1001 00
0010 10 1010 01
𝑆1 = 0011 11 1011 00
0100 10 1100 10
0101 00 1101 01
0110 01 1110 00
0111 11 1111 11
Denote the 4 bits produced by 𝑆0 and 𝑆1 as 𝑏0 𝑏1 𝑏2 𝑏3 . This 4-bit string undergoes another permutation
called 𝑃4 as follows:
𝑃4 (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 ) = (𝑏1 , 𝑏3 , 𝑏2 , 𝑏0 )
49
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: B = "10111101"
sage: K = "11010101"
sage: B = sdes.string_to_list(B); K = sdes.string_to_list(K)
sage: sdes.permute_substitute(B, K)
[1, 0, 1, 0, 1, 1, 0, 1]
random_key()
Return a random 10-bit key.
EXAMPLES:
The size of each key is the same as the block size:
sbox()
Return the S-boxes of simplified DES.
EXAMPLES:
string_to_list(S)
Return a list representation of the binary string S.
INPUT:
• S – a string of bits
OUTPUT:
A list representation of the string S.
EXAMPLES:
A list representation of a string of bits:
subkey(K, n=1)
Return the n-th subkey based on the key K.
INPUT:
• K – a 10-bit secret key of this Simplified DES
• n – (default: 1) if n=1 then return the first subkey based on K; if n=2 then return the second subkey.
The valid values for n are 1 and 2, since only two subkeys are defined for each secret key in Schaefer’s
S-DES.
OUTPUT:
The n-th subkey based on the secret key K.
EXAMPLES:
Obtain the first subkey from a secret key:
sage: K = "1010010010"
sage: L = sdes.string_to_list(K)
sage: sdes.subkey(L, n=1)
[1, 0, 1, 0, 0, 1, 0, 1]
sage: sdes.subkey(sdes.string_to_list("0010010011"), n=2)
[0, 1, 1, 0, 1, 0, 1, 0]
switch(B)
Interchange the first 4 bits with the last 4 bits in the list B of 8 bits. Let (𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 ) be a
vector of 8 bits, where each 𝑏𝑖 ∈ {0, 1}. Then the switch function 𝜎 is given by
𝜎(𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 , 𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 ) = (𝑏4 , 𝑏5 , 𝑏6 , 𝑏7 , 𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 )
INPUT:
51
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: S = "11101000"
sage: L = sdes.string_to_list(S)
sage: sdes.switch(L)
[1, 0, 0, 0, 1, 1, 1, 0]
sage: sdes.switch(sdes.string_to_list("11110000"))
[0, 0, 0, 0, 1, 1, 1, 1]
SIX
MINI-AES
A simplified variant of the Advanced Encryption Standard (AES). Note that Mini-AES is for educational purposes
only. It is a small-scale version of the AES designed to help beginners understand the basic structure of AES.
AUTHORS:
• Minh Van Nguyen (2009-05): initial version
class sage.crypto.block_cipher.miniaes.MiniAES
Bases: sage.structure.sage_object.SageObject
This class implements the Mini Advanced Encryption Standard (Mini-AES) described in [Pha2002]. Note that
Phan’s Mini-AES is for educational purposes only and is not secure for practical purposes. Mini-AES is a
version of the AES with all parameters significantly reduced, but at the same time preserving the structure of
AES. The goal of Mini-AES is to allow a beginner to understand the structure of AES, thus laying a foundation
for a thorough study of AES. Its goal is as a teaching tool and is different from the SR small scale variants of
the AES. SR defines a family of parameterizable variants of the AES suitable as a framework for comparing
different cryptanalytic techniques that can be brought to bear on the AES.
EXAMPLES:
Encrypt a plaintext:
[ x^3 + x x^2 + 1]
[ x^2 + x x^3 + x^2]
sage: key = MS([K("x^3 + x^2"), K("x^3 + x"), K("x^3 + x^2 + x"), K("x^2 + x + 1
˓→")]); key
[ x x^2 + x]
[x^3 + x^2 + x x^3 + x]
53
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[ x^3 + x x^2 + 1]
[ x^2 + x x^3 + x^2]
sage: plaintxt == P
True
Generate some random plaintext and a random secret key. Encrypt the plaintext using that secret key and decrypt
the result. Then compare the decrypted plaintext with the original plaintext:
GF_to_binary(G)
Return the binary representation of G. If G is an element of the finite field F24 , then obtain the binary
representation of G. If G is a list of elements belonging to F24 , obtain the 4-bit representation of each
54 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
element of the list, then concatenate the resulting 4-bit strings into a binary string. If G is a matrix with
entries over F24 , convert each matrix entry to its 4-bit representation, then concatenate the 4-bit strings.
The concatenation is performed starting from the top-left corner of the matrix, working across left to right,
top to bottom. Each element of F24 can be associated with a unique 4-bit string according to the following
table:
4-bit string F24 4-bit string F24
0000 0 1000 𝑥3
0001 1 1001 𝑥3 + 1
0010 𝑥 1010 𝑥3 + 𝑥
0011 𝑥+1 1011 𝑥3 + 𝑥 + 1
0100 𝑥2 1100 𝑥3 + 𝑥2
0101 𝑥2 + 1 1101 𝑥3 + 𝑥2 + 1
0110 𝑥2 + 𝑥 1110 𝑥3 + 𝑥2 + 𝑥
0111 𝑥2 + 𝑥 + 1 1111 𝑥3 + 𝑥2 + 𝑥 + 1
INPUT:
• G – an element of F24 , a list of elements of F24 , or a matrix over F24
OUTPUT:
• A binary string representation of G.
EXAMPLES:
Obtain the binary representation of all elements of F24 :
[0000,
0001,
0010,
0011,
0100,
0101,
0110,
0111,
1000,
1001,
1010,
1011,
1100,
1101,
1110,
1111]
55
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[ x^3 + x^2 x + 1]
[ x^2 + x + 1 x^3 + x^2 + x]
sage: maes.GF_to_binary(G)
1100001101111110
sage: MS = MatrixSpace(K, 2, 4)
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
GF_to_integer(G)
Return the integer representation of the finite field element G. If G is an element of the finite field F24 ,
then obtain the integer representation of G. If G is a list of elements belonging to F24 , obtain the integer
representation of each element of the list, and return the result as a list of integers. If G is a matrix with
entries over F24 , convert each matrix entry to its integer representation, and return the result as a list of
integers. The resulting list is obtained by starting from the top-left corner of the matrix, working across
left to right, top to bottom. Each element of F24 can be associated with a unique integer according to the
following table:
INPUT:
• G – an element of F24 , a list of elements belonging to F24 , or a matrix over F24
OUTPUT:
• The integer representation of G.
EXAMPLES:
Obtain the integer representation of all elements of F24 :
56 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: maes.GF_to_integer(G)
[7, 12, 2, 11, 15, 6, 1, 7]
[ x^3 + x^2 x + 1]
[ x^2 + x + 1 x^3 + x^2 + x]
sage: maes.GF_to_integer(G)
[12, 3, 7, 14]
sage: MS = MatrixSpace(K, 2, 4)
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
add_key(block, rkey)
Return the matrix addition of block and rkey. Both block and rkey are 2 × 2 matrices over the finite
field F24 . This method just return the matrix addition of these two matrices.
INPUT:
• block – a 2 × 2 matrix with entries over F24
• rkey – a round key; a 2 × 2 matrix with entries over F24
OUTPUT:
• The matrix addition of block and rkey.
EXAMPLES:
We can work with elements of F24 :
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
(continues on next page)
57
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[ x^3 + x x^2 + 1]
[ x + 1 x^3 + x^2]
[ x^2 + 1 x^2 + x + 1]
[ x^2 + x x^2 + 1]
sage: key = bin.encoding("KY"); key
0100101101011001
sage: key = MS(maes.binary_to_GF(key)); key
[ x^2 x^3 + x + 1]
[ x^2 + 1 x^3 + 1]
sage: maes.add_key(B, key)
[ 1 x^3 + x^2]
[ x + 1 x^3 + x^2]
[ x x + 1]
[ x^2 + 1 x^2 + x + 1]
sage: key = MS(maes.integer_to_GF(key)); key
[ x^3 + 1 x^3 + x + 1]
[ x^3 + x^2 + 1 x^3 + x^2 + x + 1]
sage: maes.add_key(N, key)
[x^3 + x + 1 x^3]
[ x^3 x^3]
binary_to_GF(B)
58 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Return a list of elements of F24 that represents the binary string B. The number of bits in B must be greater
than zero and a multiple of 4. Each nibble (or 4-bit string) is uniquely associated with an element of F24
as specified by the following table:
INPUT:
• B – a binary string, where the number of bits is positive and a multiple of 4
OUTPUT:
• A list of elements of the finite field F24 that represent the binary string B.
EXAMPLES:
Obtain all the elements of the finite field F24 :
sage: maes.binary_to_GF(B)
[0,
1,
x,
x + 1,
x^2,
x^2 + 1,
x^2 + x,
x^2 + x + 1,
x^3,
x^3 + 1,
x^3 + x,
x^3 + x + 1,
x^3 + x^2,
x^3 + x^2 + 1,
x^3 + x^2 + x,
x^3 + x^2 + x + 1]
binary_to_integer(B)
Return a list of integers representing the binary string B. The number of bits in B must be greater than zero
and a multiple of 4. Each nibble (or 4-bit string) is uniquely associated with an integer as specified by the
59
Sage 9.1 Reference Manual: Cryptography, Release 9.1
following table:
INPUT:
• B – a binary string, where the number of bits is positive and a multiple of 4
OUTPUT:
• A list of integers that represent the binary string B.
EXAMPLES:
Obtain the integer representation of every 4-bit string:
sage: maes.binary_to_integer(B)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
block_length()
Return the block length of Phan’s Mini-AES block cipher. A key in Phan’s Mini-AES is a block of 16 bits.
Each nibble of a key can be considered as an element of the finite field F24 . Therefore the key consists of
four elements from F24 .
OUTPUT:
• The block (or key) length in number of bits.
EXAMPLES:
decrypt(C, key)
Use Phan’s Mini-AES to decrypt the ciphertext C with the secret key key. Both C and key must be 2 × 2
matrices over the finite field F24 . Let 𝛾 denote the operation of nibble-sub, 𝜋 denote shift-row, 𝜃 denote
mix-column, and 𝜎𝐾𝑖 denote add-key with the round key 𝐾𝑖 . Then decryption 𝐷 using Phan’s Mini-AES
is the function composition
where 𝛾 −1 is the nibble-sub operation that uses the S-box for decryption, and the order of execution is
from right to left.
INPUT:
60 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[ x^3 + 1 x^2 + x]
[x^3 + x^2 x + 1]
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0
˓→")] ]); key
[ x^3 + 1 x^2 + x]
[x^3 + x^2 x + 1]
[ x^3 + 1 x^2 + x]
[x^3 + x^2 x + 1]
sage: plaintxt == P
True
[x^2 + x x^2]
[x^2 + x x^2 + 1]
sage: key = bin.encoding("ke"); key
0110101101100101
sage: key = MS(maes.binary_to_GF(key)); key
[ x^2 + x x^3 + x + 1]
[ x^2 + x x^2 + 1]
sage: C = maes.encrypt(P, key)
sage: plaintxt = maes.decrypt(C, key)
(continues on next page)
61
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[ x + 1 x^2 + 1]
[ x^2 + x + 1 x^3 + x^2 + x]
sage: key = MS(maes.integer_to_GF(key)); key
[ x x^2 + x]
[x^2 + x + 1 x^3]
sage: C = maes.encrypt(P, key)
sage: plaintxt = maes.decrypt(C, key)
sage: plaintxt == P
True
encrypt(P, key)
Use Phan’s Mini-AES to encrypt the plaintext P with the secret key key. Both P and key must be 2 × 2
matrices over the finite field F24 . Let 𝛾 denote the operation of nibble-sub, 𝜋 denote shift-row, 𝜃 denote
mix-column, and 𝜎𝐾𝑖 denote add-key with the round key 𝐾𝑖 . Then encryption 𝐸 using Phan’s Mini-AES
is the function composition
where the order of execution is from right to left. Note that 𝛾 is the nibble-sub operation that uses the
S-box for encryption.
INPUT:
• P – a plaintext block; must be a 2 × 2 matrix over the finite field F24
• key – a secret key for this Mini-AES block cipher; must be a 2 × 2 matrix over the finite field F24
OUTPUT:
• The ciphertext corresponding to P.
EXAMPLES:
Here we work with elements of F24 :
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
sage: K = FiniteField(16, "x")
sage: MS = MatrixSpace(K, 2, 2)
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ]);
˓→P
[ x^3 + 1 x^2 + x]
[x^3 + x^2 x + 1]
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0
˓→")] ]); key
62 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[x^2 + x x^2]
[x^2 + x x^2 + 1]
sage: key = bin.encoding("ke"); key
0110101101100101
sage: key = MS(maes.binary_to_GF(key)); key
[ x^2 + x x^3 + x + 1]
[ x^2 + x x^2 + 1]
sage: C = maes.encrypt(P, key)
sage: plaintxt = maes.decrypt(C, key)
sage: plaintxt == P
True
[ 1 x^2 + 1]
[ x^3 x^3 + x^2]
sage: key = MS(maes.integer_to_GF(key)); key
[ x^2 + 1 x^3 + 1]
[x^3 + x^2 + x + 1 0]
sage: C = maes.encrypt(P, key)
sage: plaintxt = maes.decrypt(C, key)
sage: plaintxt == P
True
integer_to_GF(N)
Return the finite field representation of N. If 𝑁 is an integer such that 0 ≤ 𝑁 ≤ 15, return the element
of F24 that represents N. If N is a list of integers each of which is ≥ 0 and ≤ 15, then obtain the element
of F24 that represents each such integer, and return a list of such finite field representations. Each integer
between 0 and 15, inclusive, can be associated with a unique element of F24 according to the following
63
Sage 9.1 Reference Manual: Cryptography, Release 9.1
table:
integer F24 integer F24
0 0 8 𝑥3
1 1 9 𝑥3 + 1
2 𝑥 10 𝑥3 + 𝑥
3 𝑥+1 11 𝑥3 + 𝑥 + 1
4 𝑥2 12 𝑥3 + 𝑥2
5 𝑥2 + 1 13 𝑥3 + 𝑥2 + 1
6 𝑥2 + 𝑥 14 𝑥3 + 𝑥2 + 𝑥
7 𝑥2 + 𝑥 + 1 15 𝑥3 + 𝑥2 + 𝑥 + 1
INPUT:
• N – a non-negative integer less than or equal to 15, or a list of such integers
OUTPUT:
• Elements of the finite field F24 .
EXAMPLES:
Obtain the element of F24 representing an integer 𝑛, where 0 ≤ 𝑛 ≤ 15:
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
sage: maes.integer_to_GF(0)
0
sage: maes.integer_to_GF(2)
x
sage: maes.integer_to_GF(7)
x^2 + x + 1
Obtain the finite field elements corresponding to all non-negative integers less than or equal to 15:
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
sage: lst = [n for n in range(16)]; lst
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
sage: maes.integer_to_GF(lst)
[0,
1,
x,
x + 1,
x^2,
x^2 + 1,
x^2 + x,
x^2 + x + 1,
x^3,
x^3 + 1,
x^3 + x,
x^3 + x + 1,
x^3 + x^2,
x^3 + x^2 + 1,
x^3 + x^2 + x,
x^3 + x^2 + x + 1]
integer_to_binary(N)
Return the binary representation of N. If 𝑁 is an integer such that 0 ≤ 𝑁 ≤ 15, return the binary represen-
tation of N. If N is a list of integers each of which is ≥ 0 and ≤ 15, then obtain the binary representation of
64 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
each integer, and concatenate the individual binary representations into a single binary string. Each integer
between 0 and 15, inclusive, can be associated with a unique 4-bit string according to the following table:
INPUT:
• N – a non-negative integer less than or equal to 15, or a list of such integers
OUTPUT:
• A binary string representing N.
EXAMPLES:
The binary representations of all integers between 0 and 15, inclusive:
mix_column(block)
Return the matrix multiplication of block with a constant matrix. The constant matrix is
[︂ ]︂
𝑥+1 𝑥
𝑥 𝑥+1
INPUT:
65
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[ x^3 + x 0]
[ x^2 + 1 x^3 + x^2 + x + 1]
Multiplying by the identity matrix should leave the constant matrix unchanged:
[x + 1 x]
[ x x + 1]
[x^2 + x + 1 x]
[ x^2 + 1 x^2]
sage: maes.mix_column(B)
[ x + 1 x^3 + x^2 + x]
[ 1 x^3]
[ x^3 + x x^2 + 1]
[ x x^2 + x + 1]
sage: maes.mix_column(P)
[x^3 + 1 1]
[ 1 x + 1]
nibble_sub(block, algorithm=’encrypt’)
66 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Input Output
0 𝑥3 + 𝑥2 + 𝑥
1 𝑥2
𝑥 𝑥 3 + 𝑥2 + 1
𝑥+1 1
𝑥2 𝑥
𝑥2 + 1 𝑥3 + 𝑥2 + 𝑥 + 1
𝑥2 + 𝑥 𝑥3 + 𝑥 + 1
𝑥2 + 𝑥 + 1 𝑥3
𝑥3 𝑥+1
𝑥3 + 1 𝑥3 + 𝑥
𝑥3 + 𝑥 𝑥2 + 𝑥
𝑥3 + 𝑥 + 1 𝑥3 + 𝑥2
𝑥3 + 𝑥2 𝑥2 + 1
𝑥3 + 𝑥2 + 1 𝑥3 + 1
𝑥3 + 𝑥2 + 𝑥 0
𝑥3 + 𝑥2 + 𝑥 + 1 𝑥2 + 𝑥 + 1
Note that the above S-box is used for encryption. The S-box for decryption is obtained from the above S-
box by reversing the role of the Input and Output columns. Thus the previous Input column for encryption
now becomes the Output column for decryption, and the previous Output column for encryption is now
the Input column for decryption. The S-box used for decryption can be specified as:
Input Output
0 𝑥3 + 𝑥2 + 𝑥
1 𝑥+1
𝑥 𝑥2
𝑥+1 𝑥3
𝑥2 1
𝑥2 + 1 𝑥3 + 𝑥2
𝑥2 + 𝑥 𝑥3 + 𝑥
𝑥2 + 𝑥 + 1 𝑥3 + 𝑥2 + 𝑥 + 1
𝑥3 𝑥2 + 𝑥 + 1
𝑥3 + 1 𝑥3 + 𝑥2 + 1
𝑥3 + 𝑥 𝑥3 + 1
𝑥3 + 𝑥 + 1 𝑥2 + 𝑥
𝑥3 + 𝑥2 𝑥3 + 𝑥 + 1
𝑥3 + 𝑥2 + 1 𝑥
𝑥3 + 𝑥2 + 𝑥 0
𝑥3 + 𝑥2 + 𝑥 + 1 𝑥2 + 1
67
Sage 9.1 Reference Manual: Cryptography, Release 9.1
INPUT:
• block – a 2 × 2 matrix with entries over F24
• algorithm – (default: "encrypt") a string; a flag to signify whether this nibble-sub operation
is used for encryption or decryption. The encryption flag is "encrypt" and the decryption flag is
"decrypt".
OUTPUT:
• A 2 × 2 matrix resulting from applying an S-box on entries of the 2 × 2 matrix block.
EXAMPLES:
Here we work with elements of the finite field F24 :
[x^2 + x x]
[x^2 + x x^3 + 1]
sage: maes.nibble_sub(B, algorithm="encrypt")
[ x^3 + x x^2]
[ x^3 + x x^3 + x^2 + 1]
[ x x^2 + x]
[ x^3 x^3 + x^2 + x]
sage: maes.nibble_sub(P, algorithm="encrypt")
68 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
random_key()
A random key within the key space of this Mini-AES block cipher. Like the AES, Phan’s Mini-AES is a
symmetric-key block cipher. A Mini-AES key is a block of 16 bits, or a 2 × 2 matrix with entries over the
finite field F24 . Thus the number of possible keys is 216 = 164 .
OUTPUT:
• A 2 × 2 matrix over the finite field F24 , used as a secret key for this Mini-AES block cipher.
EXAMPLES:
Each nibble of a key is an element of the finite field F24 :
sage: K = FiniteField(16, "x")
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
sage: key = maes.random_key()
sage: [key[i][j] in K for i in range(key.nrows()) for j in range(key.ncols())]
[True, True, True, True]
Generate a random key, then perform encryption and decryption using that key:
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
sage: K = FiniteField(16, "x")
sage: MS = MatrixSpace(K, 2, 2)
sage: key = maes.random_key()
sage: P = MS.random_element()
sage: C = maes.encrypt(P, key)
sage: plaintxt = maes.decrypt(C, key)
sage: plaintxt == P
True
round_key(key, n)
Return the round key for round n. Phan’s Mini-AES is defined to have two rounds. The round key 𝐾0
is generated and used prior to the first round, with round keys 𝐾1 and 𝐾2 being used in rounds 1 and 2
respectively. In total, there are three round keys, each generated from the secret key key.
INPUT:
• key – the secret key
• n – non-negative integer; the round number
OUTPUT:
• The 𝑛-th round key.
EXAMPLES:
Obtaining the round keys from the secret key:
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
sage: K = FiniteField(16, "x")
sage: MS = MatrixSpace(K, 2, 2)
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0
˓→")] ]) (continues on next page)
69
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[ x + 1 x^3 + x^2 + x + 1]
[ 0 x^3 + x^2 + x + 1]
sage: maes.round_key(key, 2)
[x^2 + x x^3 + 1]
[x^2 + x x^2 + x]
sbox()
Return the S-box of Mini-AES.
EXAMPLES:
sage: from sage.crypto.block_cipher.miniaes import MiniAES
sage: maes = MiniAES()
sage: maes.sbox()
(14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7)
shift_row(block)
Rotate each row of block to the left by different nibble amounts. The first or zero-th row is left un-
changed, while the second or row one is rotated left by one nibble. This has the effect of only interchanging
the nibbles in the second row. Let 𝑏0 , 𝑏1 , 𝑏2 , 𝑏3 be four nibbles arranged as the following 2 × 2 matrix
[︂ ]︂
𝑏0 𝑏2
𝑏1 𝑏3
sage: maes.shift_row(mat)
(continues on next page)
70 Chapter 6. Mini-AES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
[x^3 + x^2 + x + 1 0]
[ x^3 + x x^2 + x + 1]
sage: mat
[x^3 + x^2 + x + 1 0]
[ x^2 + x + 1 x^3 + x]
[ x^2 + 1 1]
[x^2 + x + 1 x^2]
sage: maes.shift_row(B)
[ x^2 + 1 1]
[ x^2 x^2 + x + 1]
[ x + 1 x^2 + x]
[ x^3 + 1 x^3 + x^2]
sage: maes.shift_row(P)
[ x + 1 x^2 + x]
[x^3 + x^2 x^3 + 1]
71
Sage 9.1 Reference Manual: Cryptography, Release 9.1
72 Chapter 6. Mini-AES
CHAPTER
SEVEN
DES
Validate the Sample Round Outputs for DES (cf. [KeSm1998] p. 124):
sage: from sage.crypto.block_cipher.des import DES
sage: P = 0
sage: K = 0x10316E028C8F3B4A
sage: for r in range(1, 17):
....: DES(rounds=r, doFinalRound=False).encrypt(P, K).hex()
'47092b5b'
'47092b5b53f372af'
'53f372af9f1d158b'
...
'3f6c3efd5a1e5228'
sage: DES().encrypt(P, K).hex()
'82dcbafbdeab6602'
73
Sage 9.1 Reference Manual: Cryptography, Release 9.1
AUTHORS:
• Lukas Stennes (2019-03-29): initial version
class sage.crypto.block_cipher.des.DES(rounds=None, keySchedule=’DES_KS’, keySize=64,
doFinalRound=True)
Bases: sage.structure.sage_object.SageObject
This class implements DES described in [U.S1999].
EXAMPLES:
You can invoke DES encryption/decryption either by calling DES with an appropriate flag:
sage: C = des.encrypt(P, K)
sage: P == des.decrypt(C, K)
True
You can use hex (i.e. integers) or a list-like bit representation for the inputs. If the input is an integer the output
will be too. If it is list-like the output will be a bit vector:
74 Chapter 7. DES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
See also:
DES_KS sage.crypto.sboxes
__init__(rounds=None, keySchedule=’DES_KS’, keySize=64, doFinalRound=True)
Construct an instance of DES.
INPUT:
• rounds – integer (default: None); the number of rounds. If None the number of rounds of the key
schedule is used.
• keySchedule – (default: 'DES_KS'); the key schedule that will be used for encryption and de-
cryption. If 'DES_KS' the default DES key schedule is used.
• keySize – (default: 64); the key length in bits. Must be 56 of 64. In the latter case the key contains
8 parity bits.
• doFinalRound – boolean (default: True); if False a swap takes places but the inverse initial
permutation is omitted (i.e. you can get the state after rounds). This only effects encryption.
EXAMPLES:
Reducing the number of rounds is simple. But increasing it is only possible if the key schedule can produce
enough round keys:
You can use arbitrary key schedules. Since it is the only one implemented here the original key schedule
is used for demonstration:
75
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• The plaintext or ciphertext corresponding to block, obtained using key. If block is an integer the
output will be too. If block is list-like the output will be a bit vector.
EXAMPLES:
decrypt(ciphertext, key)
Return the plaintext corresponding to ciphertext, using DES decryption with key.
INPUT:
• ciphertext – integer or bit list-like; the ciphertext that will be decrypted
• key – integer or bit list-like; the key
OUTPUT:
• The plaintext corresponding to ciphertext, obtained using key. If ciphertext is an integer
the output will be too. If ciphertext is list-like the output will be a bit vector.
EXAMPLES:
Decrypt a message:
You can also use 56 bit keys i.e. you can leave out the parity bits:
encrypt(plaintext, key)
Return the ciphertext corresponding to plaintext, using DES encryption with key.
INPUT:
• plaintext – integer or bit list-like; the plaintext that will be encrypted.
• key – integer or bit list-like; the key
OUTPUT:
• The ciphertext corresponding to plaintext, obtained using key. If plaintext is an integer the
output will be too. If plaintext is list-like the output will be a bit vector.
EXAMPLES:
Encrypt a message:
76 Chapter 7. DES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
You can also use 56 bit keys i.e. you can leave out the parity bits:
round(state, round_key)
Apply one round of DES to state and return the result.
EXAMPLES:
sbox_layer(block)
Apply the Sboxes to block.
EXAMPLES:
See also:
sage.crypto.sboxes
class sage.crypto.block_cipher.des.DES_KS(rounds=16, masterKey=None)
Bases: sage.structure.sage_object.SageObject
This class implements the DES key schedules described in [U.S1999].
EXAMPLES:
Initialise the key schedule with a 𝑚𝑎𝑠𝑡𝑒𝑟𝐾𝑒𝑦 to use it as an iterable:
77
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Or omit the 𝑚𝑎𝑠𝑡𝑒𝑟𝐾𝑒𝑦 and pass a key when calling the key schedule:
sage: ks = DES_KS()
sage: K = ks(0x584023641ABA6176)
sage: K[0].hex()
'd0a2ed2fa124'
sage: K[15].hex()
'43b42af81183'
See also:
DES
__init__(rounds=16, masterKey=None)
Construct an instance of DES_KS.
INPUT:
• rounds – integer (default: 16); the number of rounds self can create keys for
• masterKey – integer or bit list-like (default: None); the 64-bit key that will be used
EXAMPLES:
Note: If you want to use a DES_KS object as an iterable you have to pass a masterKey value on
initialisation. Otherwise you can omit masterKey and pass a key when you call the object.
__call__(key)
Return all round keys in a list.
INPUT:
• key – integer or bit list-like; the 64-bit key
OUTPUT:
• A list containing the round keys. If key is an integer the elements of the output list will be too. If
key is list-like the element of the output list will be bit vectors.
EXAMPLES:
This implementation is using bit vectors for all internal representations. So you can invoke the key schedule
with a bit vector:
78 Chapter 7. DES
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: K = 0x133457799bbcdff1
sage: ks = DES_KS(16, K)
sage: [k.hex() for k in ks]
['1b02effc7072',
'79aed9dbc9e5',
...
'cb3d8b0e17f5']
Note: If you want to use a DES_KS object as an iterable you have to pass a masterKey value on
initialisation. Otherwise you can omit masterKey and pass a key when you call the object.
sage.crypto.block_cipher.des.convert_to_vector(I, L)
Convert I to a bit vector of length L.
INPUT:
• I – integer or bit list-like
• L – integer; the desired bit length of the ouput
OUTPUT:
• the L-bit vector representation of I
EXAMPLES:
79
Sage 9.1 Reference Manual: Cryptography, Release 9.1
80 Chapter 7. DES
CHAPTER
EIGHT
PRESENT
AUTHORS:
• Lukas Stennes (2019-02-01): initial version
81
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: C = present.encrypt(P, K)
sage: P == present.decrypt(C, K)
True
You can use integers or a list-like bit representation for the inputs. If the input is an integer the output will be
too. If it is list-like the output will be a bit vector:
sage: P = ZZ(0).digits(2,padto=64)
sage: K = ZZ(0).digits(2,padto=80)
sage: list(present(present(P, K, 'encrypt'), K, 'decrypt')) == P
True
sage: P = ZZ(0).digits(2,padto=64)
sage: K = 0x0
sage: list(present(present(P, K, 'encrypt'), K, 'decrypt')) == P
True
The 80-bit version of PRESENT is used by default but the 128-bit version is also implemented:
See also:
PRESENT_KS sage.crypto.sboxes
__init__(keySchedule=80, rounds=None, doFinalRound=False)
Construct an instance of PRESENT.
INPUT:
• keySchedule – (default: 80); the key schedule that will be used for encryption and decryption.
Use 80 or 128 as a shortcut for the original key schedules from [BKLPPRSV2007].
82 Chapter 8. PRESENT
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• rounds – integer (default: None); the number of rounds. If None the number of rounds of the key
schedule is used.
• doFinalRound – boolean (default: False); flag to control wether the linear layer in the last round
should take place or not. Since the last linear layer does not add any security, it usually does not take
place in real world implementations for performance reasons.
EXAMPLES:
By default a 80-bit version with 31 rounds is created:
By default the linear layer operation in the last round is omitted but of course you can enable it:
You can use arbitrary key schedules. Since it is the only one implemented here the original key schedule
is used for demonstration:
See also:
PRESENT_KS
__call__(block, key, algorithm=’encrypt’)
Apply PRESENT encryption or decryption on block using key. The flag algorithm controls what
action is to be performed on block.
83
Sage 9.1 Reference Manual: Cryptography, Release 9.1
INPUT:
• block – integer or bit list-like; the plaintext or ciphertext
• K – integer or bit list-like; the key
• algorithm – string (default: 'encrypt'); a flag to signify whether encryption or decryption is to
be applied to B. The encryption flag is 'encrypt' and the decryption flag is 'decrypt'
OUTPUT:
• The plaintext or ciphertext corresponding to block, obtained using the key. If block is an integer
the output will be too. If block is list-like the output will be a bit vector.
EXAMPLES:
decrypt(ciphertext, key)
Return the plaintext corresponding to the ciphertext, using PRESENT decryption with key.
INPUT:
• ciphertext – integer or bit list-like; the ciphertext that will be decrypted
• key – integer or bit list-like; the key
OUTPUT:
• The plaintext corresponding to ciphertext, obtained using the key. If ciphertext is an integer
the output will be too. If ciphertext is list-like the output will be a bit vector.
EXAMPLES:
The test vectors from [BKLPPRSV2007] are checked here:
84 Chapter 8. PRESENT
Sage 9.1 Reference Manual: Cryptography, Release 9.1
encrypt(plaintext, key)
Return the ciphertext corresponding to plaintext, using PRESENT encryption with key.
INPUT:
• plaintext – integer or bit list-like; the plaintext that will be encrypted.
• key – integer or bit list-like; the key
OUTPUT:
• The ciphertext corresponding to plaintext, obtained using the key. If plaintext is an integer
the output will be too. If plaintext is list-like the output will be a bit vector.
EXAMPLES:
The test vectors from [BKLPPRSV2007] are checked here:
ALGORITHM:
Description of the encryption function based on [BKLPPRSV2007]:
A top-level algorithmic description of PRESENT encryption:
generateRoundKeys()
for i = 1 to 31 do
addRoundkey(STATE, K_i)
sBoxLayer(STATE)
pLayer(STATE)
end for
addRoundkey(STATE, K_{32})
Each of the 31 rounds consists of an XOR operation to introduce a round key 𝐾𝑖 for 1 ≤ 𝑖 ≤ 32, where
𝐾32 is used for post-whitening, a linear bitwise permutation and a non-linear substitution layer. The non-
linear layer uses a single 4-bit S-box which is applied 16 times in parallel in each round. Each stage but
addRoundkey is specified in its corresponding function.
addRoundkey: Given round key 𝐾𝑖 = 𝜅𝑖63 . . . 𝜅𝑖0 for 1 ≤ 𝑖 ≤ 32 and current STATE 𝑏63 . . . 𝑏0 , ad-
dRoundkey consists of the operation for 0 ≤ 𝑗 ≤ 63, 𝑏𝑗 = 𝑏𝑗 ⊕ 𝜅𝑖𝑗 .
85
Sage 9.1 Reference Manual: Cryptography, Release 9.1
linear_layer(state, inverse=False)
Apply the pLayer of PRESENT to the bit vector state and return the result.
The bit permutation used in PRESENT is given by the following table. Bit 𝑖 of STATE is moved to bit
position 𝑃 (𝑖).
i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
P(i) 0 16 32 48 1 17 33 49 2 18 34 50 3 19 35 51
i 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
P(i) 4 20 36 52 5 21 37 53 6 22 38 54 7 23 39 55
i 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
P(i) 8 24 40 56 9 25 41 57 10 26 42 58 11 27 43 59
i 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
P(i) 12 28 44 60 13 29 45 61 14 30 46 62 15 31 47 63
EXAMPLES:
sbox_layer(state, inverse=False)
Apply the sBoxLayer of PRESENT to the bit vector state and return the result.
The S-box used in PRESENT is a 4-bit to 4-bit S-box. The action of this box in hexadecimal notation is
given by the following table.
x 0 1 2 3 4 5 6 7 8 9 A B C D E F
S[x] C 5 6 B 9 0 A D 3 E F 8 4 7 1 2
For sBoxLayer the current STATE 𝑏63 . . . 𝑏0 is considered as sixteen 4-bit words 𝑤15 . . . 𝑤0 where 𝑤𝑖 =
𝑏4𝑖+3 ||𝑏4𝑖+2 ||𝑏4𝑖+1 ||𝑏4𝑖 for 0 ≤ 𝑖 ≤ 15 and the output nibble S[𝑤𝑖 ] provides the updated state values in
the obvious way.
EXAMPLES:
86 Chapter 8. PRESENT
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Note: sage.crypto.sbox uses big endian by default whereas most of Sage uses little endian. So
to use the big endian PRESENT Sbox from sage.crypto.sboxes sbox_layer() has to do some
endian conversion (i.e. reverse input and ouput of the Sbox). Keep this in mind if you change the Sbox or
sbox_layer().
Or omit the 𝑚𝑎𝑠𝑡𝑒𝑟_𝑘𝑒𝑦 and pass a key when calling the key schedule:
sage: ks = PRESENT_KS(keysize=128)
sage: K = ks(0x00112233445566778899aabbccddeeff)
sage: K[0] == 0x0011223344556677
True
sage: K[31] == 0x091989a5ae8eab21
True
ALGORITHM:
Description of the key schedule for 64-bit and 128-bit keys from [BKLPPRSV2007]:
The key schedule for 64-bit keys works as follows:
At round 𝑖 the 64-bit round key 𝐾𝑖 = 𝜅63 𝜅62 . . . 𝜅0 consists of the 64 leftmost bits of the current contents of
register 𝐾. Thus at round 𝑖 we have that:
87
Sage 9.1 Reference Manual: Cryptography, Release 9.1
After extracting the round key 𝐾𝑖 , the key register 𝐾 = 𝑘79 𝑘78 . . . 𝑘0 is updated as follows:
Thus, the key register is rotated by 61 bit positions to the left, the left-most four bits are passed through the
PRESENT S-box, and the round_counter value 𝑖 is exclusive-ored with bits 𝑘19 𝑘18 𝑘17 𝑘16 𝑘15 of 𝐾 with the
least significant bit of round_counter on the right.
The key schedule for 128-bit keys works as follows:
At round 𝑖 the 64-bit round key 𝐾𝑖 = 𝜅63 𝜅62 . . . 𝜅0 consists of the 64 leftmost bits fo the current contents of
register 𝐾. Thus at round 𝑖 we have that:
After extracting the round key 𝐾𝑖 , the key register 𝐾 = 𝑘127 𝑘126 . . . 𝑘0 is updated as follows:
Thus, the key register is rotated by 61 bit positions to the left, the left-most eight bits are passed through two
PRESENT S-boxes, and the round_counter value 𝑖 is exclusive-ored with bits 𝑘66 𝑘65 𝑘64 𝑘63 𝑘62 of 𝐾 with the
least significant bit of round_counter on the right.
See also:
PRESENT sage.crypto.sboxes
Note: sage.crypto.sbox uses big endian by default whereas most of Sage uses little endian. So to use the
big endian PRESENT Sbox from sage.crypto.sboxes PRESENT_KS has to do some endian conversion
(i.e. reverse input and ouput of the Sbox). Keep this in mind if you change the Sbox or __call__().
Note: If you want to use a PRESENT_KS object as an iterable you have to pass a master_key value
on initialisation. Otherwise you can omit master_key and pass a key when you call the object.
88 Chapter 8. PRESENT
Sage 9.1 Reference Manual: Cryptography, Release 9.1
__call__(K)
Return all round keys in a list.
INPUT:
• K – integer or bit list-like; the key
OUTPUT:
• A list containing rounds + 1 round keys. Since addRoundkey takes place in every round and after
the last round there must be rounds + 1 round keys. If K is an integer the elements of the output
list will be too. If K is list-like the element of the output list will be bit vectors.
EXAMPLES:
Note: If you want to use a PRESENT_KS object as an iterable you have to pass a master_key value
on initialisation. Otherwise you can omit master_key and pass a key when you call the object.
sage.crypto.block_cipher.present.convert_to_vector(I, L)
Convert I to a bit vector of length L.
INPUT:
• I – integer or bit list-like
• L – integer; the desired bit length of the ouput
OUTPUT:
• the L-bit vector representation of I
EXAMPLES:
89
Sage 9.1 Reference Manual: Cryptography, Release 9.1
90 Chapter 8. PRESENT
CHAPTER
NINE
The Blum-Goldwasser probabilistic public-key encryption scheme. This scheme was originally described in
[BG1985]. See also section 8.7.2 of [MvOV1996] and the Wikipedia article Blum-Goldwasser_cryptosystem on this
scheme.
AUTHORS:
• Mike Hogan and David Joyner (2009-9-19): initial procedural version released as public domain software.
• Minh Van Nguyen (2009-12): integrate into Sage as a class and relicense under the GPLv2+. Complete rewrite
of the original version to follow the description contained in [MvOV1996].
class sage.crypto.public_key.blum_goldwasser.BlumGoldwasser
Bases: sage.crypto.cryptosystem.PublicKeyCryptosystem
The Blum-Goldwasser probabilistic public-key encryption scheme.
The Blum-Goldwasser encryption and decryption algorithms as described in encrypt() and decrypt(),
respectively, make use of the least significant bit of a binary string. A related concept is the 𝑘 least significant
bits of a binary string. For example, given a positive integer 𝑛, let 𝑏 = 𝑏0 𝑏1 · · · 𝑏𝑚−1 be the binary representation
of 𝑛 so that 𝑏 is a binary string of length 𝑚. Then the least significant bit of 𝑛 is 𝑏𝑚−1 . If 0 < 𝑘 ≤ 𝑚, then
the 𝑘 least significant bits of 𝑛 are 𝑏𝑚−1−𝑘 𝑏𝑚−𝑘 · · · 𝑏𝑚−1 . The least significant bit of an integer is also referred
to as its parity bit, because this bit determines whether the integer is even or odd. In the following example, we
obtain the least significant bit of an integer:
sage: n = 123
sage: b = n.binary(); b
'1111011'
sage: n % 2
1
sage: b[-1]
'1'
91
Sage 9.1 Reference Manual: Cryptography, Release 9.1
EXAMPLES:
The following encryption/decryption example is taken from Example 8.57, pages 309–310 of [MvOV1996]:
Generate a pair of random public/private keys. Use the public key to encrypt a plaintext. Then decrypt the
resulting ciphertext using the private key. Finally, compare the decrypted message with the original plaintext.
If (𝑝, 𝑞, 𝑎, 𝑏) is a private key, then 𝑛 = 𝑝𝑞 is the corresponding public key. Furthermore, we have gcd(𝑝, 𝑞) =
𝑎𝑝 + 𝑏𝑞 = 1.
sage: p, q, a, b = prikey
sage: pubkey == p * q
True
sage: gcd(p, q) == a*p + b*q == 1
True
decrypt(C, K)
Apply the Blum-Goldwasser scheme to decrypt the ciphertext C using the private key K.
INPUT:
• C – a ciphertext resulting from encrypting a plaintext using the Blum-Goldwasser encryption algo-
rithm. The ciphertext 𝐶 must be of the form 𝐶 = (𝑐1 , 𝑐2 , . . . , 𝑐𝑡 , 𝑥𝑡+1 ). Each 𝑐𝑖 is a sub-block of
binary string and 𝑥𝑡+1 is the result of the 𝑡 + 1-th iteration of the Blum-Blum-Shub algorithm.
• K – a private key (𝑝, 𝑞, 𝑎, 𝑏) where 𝑝 and 𝑞 are distinct Blum primes and gcd(𝑝, 𝑞) = 𝑎𝑝 + 𝑏𝑞 = 1.
OUTPUT:
• The plaintext resulting from decrypting the ciphertext C using the Blum-Goldwasser decryption algo-
rithm.
ALGORITHM:
The Blum-Goldwasser decryption algorithm is described in Algorithm 8.56, page 309 of [MvOV1996].
The algorithm works as follows:
1. Let 𝐶 be the ciphertext 𝐶 = (𝑐1 , 𝑐2 , . . . , 𝑐𝑡 , 𝑥𝑡+1 ). Then 𝑡 is the number of ciphertext sub-blocks and
ℎ is the length of each binary string sub-block 𝑐𝑖 .
2. Let (𝑝, 𝑞, 𝑎, 𝑏) be the private key whose corresponding public key is 𝑛 = 𝑝𝑞. Note that gcd(𝑝, 𝑞) =
𝑎𝑝 + 𝑏𝑞 = 1.
3. Compute 𝑑1 = ((𝑝 + 1)/4)𝑡+1 mod (𝑝 − 1).
4. Compute 𝑑2 = ((𝑞 + 1)/4)𝑡+1 mod (𝑞 − 1).
5. Let 𝑢 = 𝑥𝑑𝑡+1
1
mod 𝑝.
6. Let 𝑣 = 𝑥𝑑𝑡+1
2
mod 𝑞.
7. Compute 𝑥0 = 𝑣𝑎𝑝 + 𝑢𝑏𝑞 mod 𝑛.
8. For 𝑖 from 1 to 𝑡, do:
1. Compute 𝑥𝑖 = 𝑥2𝑡−1 mod 𝑛.
2. Let 𝑝𝑖 be the ℎ least significant bits of 𝑥𝑖 .
3. Compute 𝑚𝑖 = 𝑝𝑖 ⊕ 𝑐𝑖 .
9. The plaintext is 𝑚 = 𝑚1 𝑚2 · · · 𝑚𝑡 .
EXAMPLES:
The following decryption example is taken from Example 8.57, pages 309–310 of [MvOV1996]. Here we
decrypt a binary string:
Decrypt a longer ciphertext and convert the resulting plaintext into an ASCII string:
93
Sage 9.1 Reference Manual: Cryptography, Release 9.1
encrypt(P, K, seed=None)
Apply the Blum-Goldwasser scheme to encrypt the plaintext P using the public key K.
INPUT:
• P – a non-empty string of plaintext. The string "" is an empty string, whereas " " is a string
consisting of one white space character. The plaintext can be a binary string or a string of ASCII
characters. Where P is an ASCII string, then P is first encoded as a binary string prior to encryption.
• K – a public key, which is the product of two Blum primes.
• seed – (default: None) if 𝑝 and 𝑞 are Blum primes and 𝑛 = 𝑝𝑞 is a public key, then seed is
a quadratic residue in the multiplicative group (Z/𝑛Z)* . If seed=None, then the function would
generate its own random quadratic residue in (Z/𝑛Z)* . Where a value for seed is provided, it is
your responsibility to ensure that the seed is a quadratic residue in the multiplicative group (Z/𝑛Z)* .
OUTPUT:
• The ciphertext resulting from encrypting P using the public key K. The ciphertext 𝐶 is of the form
𝐶 = (𝑐1 , 𝑐2 , . . . , 𝑐𝑡 , 𝑥𝑡+1 ). Each 𝑐𝑖 is a sub-block of binary string and 𝑥𝑡+1 is the result of the 𝑡 + 1-th
iteration of the Blum-Blum-Shub algorithm.
ALGORITHM:
The Blum-Goldwasser encryption algorithm is described in Algorithm 8.56, page 309 of [MvOV1996].
The algorithm works as follows:
1. Let 𝑛 be a public key, where 𝑛 = 𝑝𝑞 is the product of two distinct Blum primes 𝑝 and 𝑞.
2. Let 𝑘 = ⌊log2 (𝑛)⌋ and ℎ = ⌊log2 (𝑘)⌋.
3. Let 𝑚 = 𝑚1 𝑚2 · · · 𝑚𝑡 be the message (plaintext) where each 𝑚𝑖 is a binary string of length ℎ.
4. Choose a random seed 𝑥0 , which is a quadratic residue in the multiplicative group (Z/𝑛Z)* . That is,
choose a random 𝑟 ∈ (Z/𝑛Z)* and compute 𝑥0 = 𝑟2 mod 𝑛.
5. For 𝑖 from 1 to 𝑡, do:
1. Let 𝑥𝑖 = 𝑥2𝑖−1 mod 𝑛.
2. Let 𝑝𝑖 be the ℎ least significant bits of 𝑥𝑖 .
3. Let 𝑐𝑖 = 𝑝𝑖 ⊕ 𝑚𝑖 .
6. Compute 𝑥𝑡+1 = 𝑥2𝑡 mod 𝑛.
7. The ciphertext is 𝑐 = (𝑐1 , 𝑐2 , . . . , 𝑐𝑡 , 𝑥𝑡+1 ).
The value ℎ in the algorithm is the sub-block length. If the binary string representing the message cannot
be divided into blocks of length ℎ each, then other sub-block lengths would be used instead. The sub-block
lengths to fall back on are in the following order: 16, 8, 4, 2, 1.
EXAMPLES:
The following encryption example is taken from Example 8.57, pages 309–310 of [MvOV1996]. Here, we
encrypt a binary string:
Now encrypt an ASCII string. The result is random; no seed is provided to the encryption function so the
function generates its own random seed:
private_key(p, q)
Return the Blum-Goldwasser private key corresponding to the distinct Blum primes p and q.
INPUT:
• p – a Blum prime.
• q – a Blum prime.
OUTPUT:
• The Blum-Goldwasser private key (𝑝, 𝑞, 𝑎, 𝑏) where gcd(𝑝, 𝑞) = 𝑎𝑝 + 𝑏𝑞 = 1.
Both p and q must be distinct Blum primes. Let 𝑝 be a positive prime. Then 𝑝 is a Blum prime if 𝑝 is
congruent to 3 modulo 4, i.e. 𝑝 ≡ 3 (mod 4).
EXAMPLES:
Obtain two distinct Blum primes and compute the Blum-Goldwasser private key corresponding to those
two Blum primes:
95
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Choose two distinct random Blum primes, compute the Blum-Goldwasser private key corresponding to
those two primes, and test that the resulting private key (𝑝, 𝑞, 𝑎, 𝑏) satisfies gcd(𝑝, 𝑞) = 𝑎𝑝 + 𝑏𝑞 = 1:
sage: from sage.crypto.util import random_blum_prime
sage: p = random_blum_prime(10**4, 10**5)
sage: q = random_blum_prime(10**4, 10**5)
sage: while q == p:
....: q = random_blum_prime(10**4, 10**5)
sage: p, q, a, b = bg.private_key(p, q)
sage: gcd(p, q) == a*p + b*q == 1
True
public_key(p, q)
Return the Blum-Goldwasser public key corresponding to the distinct Blum primes p and q.
INPUT:
• p – a Blum prime.
• q – a Blum prime.
OUTPUT:
• The Blum-Goldwasser public key 𝑛 = 𝑝𝑞.
Both p and q must be distinct Blum primes. Let 𝑝 be a positive prime. Then 𝑝 is a Blum prime if 𝑝 is
congruent to 3 modulo 4, i.e. 𝑝 ≡ 3 (mod 4).
EXAMPLES:
Obtain two distinct Blum primes and compute the Blum-Goldwasser public key corresponding to those
two Blum primes:
sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser
sage: from sage.crypto.util import is_blum_prime
sage: bg = BlumGoldwasser()
sage: P = primes_first_n(10); P
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
sage: [is_blum_prime(_) for _ in P]
[False, True, False, True, True, False, False, True, True, False]
sage: bg.public_key(3, 7)
21
Choose two distinct random Blum primes, compute the Blum-Goldwasser public key corresponding to
those two primes, and test that the public key factorizes into Blum primes:
sage: from sage.crypto.util import random_blum_prime
sage: p = random_blum_prime(10**4, 10**5)
sage: q = random_blum_prime(10**4, 10**5)
sage: while q == p:
(continues on next page)
Note: Beware that there might not be any primes between the lower and upper bounds. So make sure
that these two bounds are “sufficiently” far apart from each other for there to be primes congruent to 3
modulo 4. In particular, there should be at least two distinct primes within these bounds, each prime being
congruent to 3 modulo 4.
EXAMPLES:
Choosing a random pair of public and private keys. We then test to see if they satisfy the requirements of
the Blum-Goldwasser scheme:
97
Sage 9.1 Reference Manual: Cryptography, Release 9.1
TEN
STREAM CRYPTOSYSTEMS
class sage.crypto.stream.LFSRCryptosystem(field=None)
Bases: sage.crypto.cryptosystem.SymmetricKeyCryptosystem
Linear feedback shift register cryptosystem class
encoding(M)
class sage.crypto.stream.ShrinkingGeneratorCryptosystem(field=None)
Bases: sage.crypto.cryptosystem.SymmetricKeyCryptosystem
Shrinking generator cryptosystem class
encoding(M)
sage.crypto.stream.blum_blum_shub(length, seed=None, p=None, q=None, lbound=None,
ubound=None, ntries=100)
The Blum-Blum-Shub (BBS) pseudorandom bit generator.
See the original paper by Blum, Blum and Shub [BBS1986]. The BBS algorithm is also discussed in section
5.5.2 of [MvOV1996].
INPUT:
• length – positive integer; the number of bits in the output pseudorandom bit sequence.
• seed – (default: None) if 𝑝 and 𝑞 are Blum primes, then seed is a quadratic residue in the multiplica-
tive group (Z/𝑛Z)* where 𝑛 = 𝑝𝑞. If seed=None, then the function would generate its own random
quadratic residue in (Z/𝑛Z)* . If you provide a value for seed, then it is your responsibility to ensure that
the seed is a quadratic residue in the multiplicative group (Z/𝑛Z)* .
• p – (default: None) a large positive prime congruent to 3 modulo 4. Both p and q must be distinct. If
p=None, then a value for p will be generated, where 0 < lower_bound <= p <= upper_bound.
• q – (default: None) a large positive prime congruence to 3 modulo 4. Both p and q must be distinct. If
q=None, then a value for q will be generated, where 0 < lower_bound <= q <= upper_bound.
• lbound – (positive integer, default: None) the lower bound on how small each random primes 𝑝 and 𝑞
can be. So we have 0 < lbound <= p, q <= ubound. The lower bound must be distinct from the
upper bound.
• ubound – (positive integer, default: None) the upper bound on how large each random primes 𝑝 and 𝑞
can be. So we have 0 < lbound <= p, q <= ubound. The lower bound must be distinct from the
upper bound.
• ntries – (default: 100) the number of attempts to generate a random Blum prime. If ntries is a
positive integer, then perform that many attempts at generating a random Blum prime. This might or might
not result in a Blum prime.
OUTPUT:
99
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Note: Beware that there might not be any primes between the lower and upper bounds. So make sure that
these two bounds are “sufficiently” far apart from each other for there to be primes congruent to 3 modulo 4.
In particular, there should be at least two distinct primes within these bounds, each prime being congruent to
3 modulo 4. This function uses the function random_blum_prime() to generate random primes that are
congruent to 3 modulo 4.
ALGORITHM:
The BBS algorithm as described below is adapted from the presentation in Algorithm 5.40, page 186 of
[MvOV1996].
1. Let 𝐿 be the desired number of bits in the output bit sequence. That is, 𝐿 is the desired length of the bit
string.
2. Let 𝑝 and 𝑞 be two large distinct primes, each congruent to 3 modulo 4.
3. Let 𝑛 = 𝑝𝑞 be the product of 𝑝 and 𝑞.
4. Select a random seed value 𝑠 ∈ (Z/𝑛Z)* , where (Z/𝑛Z)* is the multiplicative group of Z/𝑛Z.
5. Let 𝑥0 = 𝑠2 mod 𝑛.
6. For 𝑖 from 1 to 𝐿, do
1. Let 𝑥𝑖 = 𝑥2𝑖−1 mod 𝑛.
2. Let 𝑧𝑖 be the least significant bit of 𝑥𝑖 .
7. The output pseudorandom bit sequence is 𝑧1 , 𝑧2 , . . . , 𝑧𝐿 .
EXAMPLES:
A BBS pseudorandom bit sequence with a specified seed:
You could specify the length of the bit string, with given values for p and q:
Or you could specify the length of the bit string, with given values for the lower and upper bounds:
Under some reasonable hypotheses, Blum-Blum-Shub [BBS1982] sketch a proof that the period of the BBS
stream cipher is equal to 𝜆(𝜆(𝑛)), where 𝜆(𝑛) is the Carmichael function of 𝑛. This is verified below in a
few examples by using the function lfsr_connection_polynomial() (written by Tim Brock) which
computes the connection polynomial of a linear feedback shift register sequence. The degree of that polynomial
is the period.
101
Sage 9.1 Reference Manual: Cryptography, Release 9.1
ELEVEN
STREAM CIPHERS
sage: FF = FiniteField(2)
sage: P.<x> = PolynomialRing(FF)
sage: E = LFSRCryptosystem(FF)
sage: E
LFSR cryptosystem over Finite Field of size 2
sage: IS = [ FF(a) for a in [0,1,1,1,0,1,1] ]
sage: g = x^7 + x + 1
sage: e = E((g,IS))
sage: B = BinaryStrings()
sage: m = B.encoding("THECATINTHEHAT")
sage: e(m)
001000110111101011101010101000110000000011010001010101110000101111001001000001111110010010001100
sage: FF = FiniteField(2)
sage: P.<x> = PolynomialRing(FF)
sage: LFSR = LFSRCryptosystem(FF)
sage: e = LFSR((x^2+x+1,[FF(0),FF(1)]))
sage: B = e.domain()
sage: m = B.encoding("The cat in the hat.")
sage: e(m)
001110011101111010111110010011011101010110111010000110011001011010110010000000111001011010101111
sage: m == e(e(m))
True
connection_polynomial()
The connection polynomial defining the LFSR of the cipher.
EXAMPLES:
sage: k = GF(2)
sage: P.<x> = PolynomialRing( k )
sage: LFSR = LFSRCryptosystem( k )
(continues on next page)
103
Sage 9.1 Reference Manual: Cryptography, Release 9.1
initial_state()
The initial state of the LFSR cipher.
EXAMPLES:
sage: k = GF(2)
sage: P.<x> = PolynomialRing( k )
sage: LFSR = LFSRCryptosystem( k )
sage: e = LFSR((x^2+x+1,[k(0), k(1)]))
sage: e.initial_state()
[0, 1]
sage: FF = FiniteField(2)
sage: P.<x> = PolynomialRing(FF)
sage: LFSR = LFSRCryptosystem(FF)
sage: IS_1 = [ FF(a) for a in [0,1,0,1,0,0,0] ]
sage: e1 = LFSR((x^7 + x + 1,IS_1))
sage: IS_2 = [ FF(a) for a in [0,0,1,0,0,0,1,0,1] ]
sage: e2 = LFSR((x^9 + x^3 + 1,IS_2))
sage: E = ShrinkingGeneratorCryptosystem()
sage: e = E((e1,e2))
sage: e
Shrinking generator cipher on Free binary string monoid
decimating_cipher()
The LFSR cipher generating the decimating key stream.
EXAMPLES:
sage: FF = FiniteField(2)
sage: P.<x> = PolynomialRing(FF)
sage: LFSR = LFSRCryptosystem(FF)
sage: IS_1 = [ FF(a) for a in [0,1,0,1,0,0,0] ]
sage: e1 = LFSR((x^7 + x + 1,IS_1))
sage: IS_2 = [ FF(a) for a in [0,0,1,0,0,0,1,0,1] ]
sage: e2 = LFSR((x^9 + x^3 + 1,IS_2))
sage: E = ShrinkingGeneratorCryptosystem()
sage: e = E((e1,e2))
sage: e.decimating_cipher()
LFSR cipher on Free binary string monoid
keystream_cipher()
The LFSR cipher generating the output key stream.
EXAMPLES:
sage: FF = FiniteField(2)
sage: P.<x> = PolynomialRing(FF)
sage: LFSR = LFSRCryptosystem(FF)
sage: IS_1 = [ FF(a) for a in [0,1,0,1,0,0,0] ]
sage: e1 = LFSR((x^7 + x + 1,IS_1))
sage: IS_2 = [ FF(a) for a in [0,0,1,0,0,0,1,0,1] ]
sage: e2 = LFSR((x^9 + x^3 + 1,IS_2))
sage: E = ShrinkingGeneratorCryptosystem()
sage: e = E((e1,e2))
sage: e.keystream_cipher()
LFSR cipher on Free binary string monoid
105
Sage 9.1 Reference Manual: Cryptography, Release 9.1
TWELVE
Stream ciphers have been used for a long time as a source of pseudo-random number generators.
S. Golomb [Go1967] gives a list of three statistical properties that a sequence of numbers a = {𝑎𝑛 }∞
𝑛=1 , 𝑎𝑛 ∈ {0, 1}
should display to be considered “random”. Define the autocorrelation of a to be
𝑁
1 ∑︁
𝐶(𝑘) = 𝐶(𝑘, a) = lim (−1)𝑎𝑛 +𝑎𝑛+𝑘 .
𝑁 →∞ 𝑁
𝑛=1
(For sequences satisfying these first two properties, it is known that 𝜖 = −1/𝑃 must hold.)
• proportional runs property: In each period, half the runs have length 1, one-fourth have length 2, etc. Moreover,
there are as many runs of 1’s as there are of 0’s.
A general feedback shift register is a map 𝑓 : F𝑑𝑞 → F𝑑𝑞 of the form
for some given constants 𝑎𝑖 ∈ F𝑞 , the map is called a linear feedback shift register (LFSR).
Example of an LFSR: Let
107
Sage 9.1 Reference Manual: Cryptography, Release 9.1
𝑓 (𝑥)
ℎ(𝑥) = = 𝑐0 + 𝑐1 𝑥 + ... + 𝑐𝑛 𝑥𝑛 + ... .
𝑔(𝑥)
We can compute a recursion formula which allows us to rapidly compute the coefficients of ℎ(𝑥) (take 𝑓 (𝑥) = 1):
𝑛
∑︁ −𝑏𝑖
𝑐𝑛 = 𝑐𝑛−𝑖 .
𝑖=1
𝑏0
The coefficients of ℎ(𝑥) can, under certain conditions on 𝑓 (𝑥) and 𝑔(𝑥), be considered “random” from certain statis-
tical points of view.
Example: For instance, if
𝑓 (𝑥) = 1, 𝑔(𝑥) = 𝑥4 + 𝑥 + 1,
then
ℎ(𝑥) = 1 + 𝑥 + 𝑥2 + 𝑥3 + 𝑥5 + 𝑥7 + 𝑥8 + ... .
The sequence of 0, 1’s is periodic with period 𝑃 = 24 − 1 = 15 and satisfies Golomb’s three randomness conditions.
However, this sequence of period 15 can be “cracked” (i.e., a procedure to reproduce 𝑔(𝑥)) by knowing only 8 terms!
This is the function of the Berlekamp-Massey algorithm [Mas1969], implemented in berlekamp_massey.py.
AUTHORS:
• David Joyner (2005-11-24): initial creation.
• Timothy Brock (2005-11): added lfsr_sequence with code modified from Python Cookbook, http://aspn.
activestate.com/ASPN/Python/Cookbook/
• Timothy Brock (2006-04-17): added lfsr_autocorrelation and lfsr_connection_polynomial.
sage.crypto.lfsr.lfsr_autocorrelation(L, p, k)
INPUT:
• L – a periodic sequence of elements of ZZ or GF(2); must have length 𝑝
• p – the period of 𝐿
• k – an integer between 0 and 𝑝
OUTPUT: autocorrelation sequence of 𝐿
EXAMPLES:
sage: F = GF(2)
sage: o = F(0)
sage: l = F(1)
sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20
sage: s = lfsr_sequence(key,fill,n)
sage: lfsr_autocorrelation(s,15,7)
4/15
sage: lfsr_autocorrelation(s,int(15),7)
4/15
108 Chapter 12. Linear feedback shift register (LFSR) sequence commands
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage.crypto.lfsr.lfsr_connection_polynomial(s)
INPUT:
• s – a sequence of elements of a finite field of even length
OUTPUT:
• C(x) – the connection polynomial of the minimal LFSR.
This implements the algorithm in section 3 of J. L. Massey’s article [Mas1969].
EXAMPLES:
sage: F = GF(2)
sage: F
Finite Field of size 2
sage: o = F(0); l = F(1)
sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20
sage: s = lfsr_sequence(key,fill,n); s
[1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
sage: lfsr_connection_polynomial(s)
x^4 + x + 1
sage: from sage.matrix.berlekamp_massey import berlekamp_massey
sage: berlekamp_massey(s)
x^4 + x^3 + 1
Notice that berlekamp_massey returns the reverse of the connection polynomial (and is potentially must
faster than this implementation).
sage.crypto.lfsr.lfsr_sequence(key, fill, n)
Create an LFSR sequence.
INPUT:
• key – a list of finite field elements, [𝑐0 , 𝑐1 , . . . , 𝑐𝑘 ]
• fill – the list of the initial terms of the LFSR sequence, [𝑥0 , 𝑥1 , . . . , 𝑥𝑘 ]
• n – number of terms of the sequence that the function returns
OUTPUT:
The LFSR sequence defined by 𝑥𝑛+1 = 𝑐𝑘 𝑥𝑛 + ... + 𝑐0 𝑥𝑛−𝑘 for 𝑛 ≥ 𝑘.
EXAMPLES:
109
Sage 9.1 Reference Manual: Cryptography, Release 9.1
110 Chapter 12. Linear feedback shift register (LFSR) sequence commands
CHAPTER
THIRTEEN
sage.crypto.util.ascii_to_bin(A)
Return the binary representation of the ASCII string A.
INPUT:
• A – a string or list of ASCII characters.
OUTPUT:
• The binary representation of A.
111
Sage 9.1 Reference Manual: Cryptography, Release 9.1
ALGORITHM:
Let 𝐴 = 𝑎0 𝑎1 · · · 𝑎𝑛−1 be an ASCII string, where each 𝑎𝑖 is an ASCII character. Let 𝑐𝑖 be the ASCII integer
corresponding to 𝑎𝑖 and let 𝑏𝑖 be the binary representation of 𝑐𝑖 . The binary representation 𝐵 of 𝐴 is 𝐵 =
𝑏0 𝑏1 · · · 𝑏𝑛−1 .
EXAMPLES:
The binary representation of some ASCII strings:
The empty string is different from the string with one space character. For the empty string and the empty list,
this function returns the same result:
This function also accepts a list of ASCII characters. You can also pass in a list of strings:
sage.crypto.util.bin_to_ascii(B)
Return the ASCII representation of the binary string B.
INPUT:
• B – a non-empty binary string or a non-empty list of bits. The number of bits in B must be a multiple of 8.
OUTPUT:
• The ASCII string corresponding to B.
ALGORITHM:
Consider a block of bits 𝐵 = 𝑏0 𝑏1 · · · 𝑏𝑛−1 where each sub-block 𝑏𝑖 is a binary string of length 8. Then the total
number of bits is a multiple of 8 and is given by 8𝑛. Let 𝑐𝑖 be the integer representation of 𝑏𝑖 . We can consider 𝑐𝑖
as the integer representation of an ASCII character. Then the ASCII representation 𝐴 of 𝐵 is 𝐴 = 𝑎0 𝑎1 · · · 𝑎𝑛−1 .
EXAMPLES:
Convert some ASCII strings to their binary representations and recover the ASCII strings from the binary rep-
resentations:
sage.crypto.util.carmichael_lambda(n)
Return the Carmichael function of a positive integer n.
The Carmichael function of 𝑛, denoted 𝜆(𝑛), is the smallest positive integer 𝑘 such that 𝑎𝑘 ≡ 1 (mod 𝑛) for
all 𝑎 ∈ Z/𝑛Z satisfying gcd(𝑎, 𝑛) = 1. Thus, 𝜆(𝑛) = 𝑘 is the exponent of the multiplicative group (Z/𝑛Z)* .
INPUT:
• n – a positive integer.
OUTPUT:
• The Carmichael function of n.
ALGORITHM:
If 𝑛 = 2, 4 then 𝜆(𝑛) = 𝜙(𝑛). Let 𝑝 ≥ 3 be an odd prime and let 𝑘 be a positive integer. Then 𝜆(𝑝𝑘 ) =
𝑝𝑘−1 (𝑝 − 1) = 𝜙(𝑝𝑘 ). If 𝑘 ≥ 3, then 𝜆(2𝑘 ) = 2𝑘−2 . Now consider the case where 𝑛 > 3 is composite and let
𝑛 = 𝑝𝑘11 𝑝𝑘22 · · · 𝑝𝑘𝑡 𝑡 be the prime factorization of 𝑛. Then
EXAMPLES:
The Carmichael function of all positive integers up to and including 10:
Cases where the Carmichael function is equivalent to the Euler phi function:
113
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Verifying the current implementation of the Carmichael function using another implementation. The other
implementation that we use for verification is an exhaustive search for the exponent of the multiplicative group
(Z/𝑛Z)* .
Carmichael’s theorem states that 𝑎𝜆(𝑛) ≡ 1 (mod 𝑛) for all elements 𝑎 of the multiplicative group (Z/𝑛Z)* .
Here, we verify Carmichael’s theorem.
REFERENCES:
• Wikipedia article Carmichael_function
sage.crypto.util.has_blum_prime(lbound, ubound)
Determine whether or not there is a Blum prime within the specified closed interval.
INPUT:
• lbound – positive integer; the lower bound on how small a Blum prime can be. The lower bound must
be distinct from the upper bound.
• ubound – positive integer; the upper bound on how large a Blum prime can be. The lower bound must be
distinct from the upper bound.
OUTPUT:
• True if there is a Blum prime p such that lbound <= p <= ubound. False otherwise.
ALGORITHM:
Let 𝐿 and 𝑈 be distinct positive integers. Let 𝑃 be the set of all odd primes 𝑝 such that 𝐿 ≤ 𝑝 ≤ 𝑈 . Our main
focus is on Blum primes, i.e. odd primes that are congruent to 3 modulo 4, so we assume that the lower bound
𝐿 > 2. The closed interval [𝐿, 𝑈 ] has a Blum prime if and only if the set 𝑃 has a Blum prime.
EXAMPLES:
Testing for the presence of Blum primes within some closed intervals. The interval [4, 100] has a Blum prime,
the smallest such prime being 7. The interval [24, 28] has no primes, hence no Blum primes.
sage.crypto.util.is_blum_prime(n)
Determine whether or not n is a Blum prime.
INPUT:
• n a positive prime.
OUTPUT:
• True if n is a Blum prime; False otherwise.
Let 𝑛 be a positive prime. Then 𝑛 is a Blum prime if 𝑛 is congruent to 3 modulo 4, i.e. 𝑛 ≡ 3 (mod 4).
EXAMPLES:
Testing some integers to see if they are Blum primes:
115
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage.crypto.util.least_significant_bits(n, k)
Return the k least significant bits of n.
INPUT:
• n – an integer.
• k – a positive integer.
OUTPUT:
• The k least significant bits of the integer n. If k=1, then return the parity bit of the integer n. Let 𝑏 be the
binary representation of n, where 𝑚 is the length of the binary string 𝑏. If 𝑘 ≥ 𝑚, then return the binary
representation of n.
EXAMPLES:
Obtain the parity bits of some integers:
sage: least_significant_bits(101, 4)
[0, 1, 0, 1]
sage: least_significant_bits(-101, 4)
[0, 1, 0, 1]
sage: least_significant_bits(124, 4)
[1, 1, 0, 0]
sage: least_significant_bits(-124, 4)
[1, 1, 0, 0]
• ntries – (default: 100) the number of attempts to generate a random Blum prime. If ntries is a
positive integer, then perform that many attempts at generating a random Blum prime. This might or might
not result in a Blum prime.
OUTPUT:
• A random Blum prime within the specified lower and upper bounds.
Note: Beware that there might not be any primes between the lower and upper bounds. So make sure that
these two bounds are “sufficiently” far apart from each other for there to be primes congruent to 3 modulo 4. In
particular, there should be at least two distinct Blum primes within the specified bounds.
EXAMPLES:
Choose a random prime and check that it is a Blum prime:
117
Sage 9.1 Reference Manual: Cryptography, Release 9.1
FOURTEEN
BOOLEAN FUNCTIONS
Those functions are used for example in LFSR based ciphers like the filter generator or the combination generator.
This module allows to study properties linked to spectral analysis, and also algebraic immunity.
EXAMPLES:
sage: R.<x>=GF(2^8,'a')[]
sage: from sage.crypto.boolean_function import BooleanFunction
sage: B = BooleanFunction( x^254 ) # the Boolean function Tr(x^254)
sage: B
Boolean function with 8 variables
sage: B.nonlinearity()
112
sage: B.algebraic_immunity()
4
AUTHOR:
• Rusydi H. Makarim (2016-10-13): add functions related to linear structures
• Rusydi H. Makarim (2016-07-09): add is_plateaued()
• Yann Laigle-Chapuy (2010-02-26): add basic arithmetic
• Yann Laigle-Chapuy (2009-08-28): first implementation
class sage.crypto.boolean_function.BooleanFunction
Bases: sage.structure.sage_object.SageObject
This module implements Boolean functions represented as a truth table.
We can construct a Boolean Function from either:
• an integer - the result is the zero function with x variables;
• a list - it is expected to be the truth table of the result. Therefore it must be of length a power of 2, and its
elements are interpreted as Booleans;
• a string - representing the truth table in hexadecimal;
• a Boolean polynomial - the result is the corresponding Boolean function;
• a polynomial P over an extension of GF(2) - the result is the Boolean function with truth table (
Tr(P(x)) for x in GF(2^k) )
EXAMPLES:
from the number of variables:
119
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: BooleanFunction([1,0,0,1])
Boolean function with 2 variables
from a string:
sage: BooleanFunction("111e")
Boolean function with 4 variables
from a sage.rings.polynomial.pbori.BooleanPolynomial:
sage: BooleanFunction(sqrt(2))
Traceback (most recent call last):
...
TypeError: unable to init the Boolean function
absolut_indicator(*args, **kwds)
Deprecated: Use absolute_indicator() instead. See trac ticket #28001 for details.
absolute_autocorrelation()
Return the absolute autocorrelation of the function.
EXAMPLES:
absolute_indicator()
Return the absolute indicator of the function.
The absolute indicator is defined as the maximal absolute value of the autocorrelation.
EXAMPLES:
sage: B.absolut_indicator()
doctest:warning
...
DeprecationWarning: absolut_indicator is deprecated. Please use absolute_
˓→indicator instead.
absolute_walsh_spectrum()
Return the absolute Walsh spectrum fo the function.
EXAMPLES:
sage: B = BooleanFunction("0113077C165E76A8")
sage: B.absolute_walsh_spectrum()
{8: 64}
algebraic_degree()
Return the algebraic degree of this Boolean function.
The algebraic degree of a Boolean function is defined as the degree of its algebraic normal form. Note that
the degree of the constant zero function is defined to be equal to -1.
EXAMPLES:
121
Sage 9.1 Reference Manual: Cryptography, Release 9.1
algebraic_immunity(annihilator=False)
Return the algebraic immunity of the Boolean function.
This is the smallest integer 𝑖 such that there exists a non trivial annihilator for 𝑠𝑒𝑙𝑓 or 𝑠𝑒𝑙𝑓 .
INPUT:
• annihilator – a Boolean (default: False), if True, returns also an annihilator of minimal degree.
EXAMPLES:
algebraic_normal_form()
Return the sage.rings.polynomial.pbori.BooleanPolynomial corresponding to the alge-
braic normal form.
EXAMPLES:
annihilator(d, dim=False)
Return (if it exists) an annihilator of the boolean function of degree at most 𝑑, that is a Boolean polynomial
𝑔 such that
𝑓 (𝑥)𝑔(𝑥) = 0∀𝑥.
INPUT:
• d – an integer;
• dim – a Boolean (default: False), if True, return also the dimension of the annihilator vector space.
EXAMPLES:
autocorrelation()
Return the autocorrelation of the function, defined by
∑︁
∆𝑓 (𝑗) = (−1)𝑓 (𝑖)⊕𝑓 (𝑖⊕𝑗) .
𝑖∈{0,1}𝑛
EXAMPLES:
correlation_immunity()
Return the maximum value 𝑚 such that the function is correlation immune of order 𝑚.
A Boolean function is said to be correlation immune of order 𝑚 , if the output of the function is statistically
independent of the combination of any m of its inputs.
EXAMPLES:
derivative(u)
Return the derivative in direction of u
INPUT:
• u – either an integer or a tuple/list of F2 elements of length equal to the number of variables
The derivative of 𝑓 in direction of 𝑢 is defined as 𝑥 ↦→ 𝑓 (𝑥) + 𝑓 (𝑥 + 𝑢).
EXAMPLES:
has_linear_structure()
Return True if this function has a linear structure.
An 𝑛-variable Boolean function 𝑓 has a linear structure if there exists a nonzero 𝑎 ∈ F𝑛2 such that 𝑓 (𝑥 ⊕
𝑎) ⊕ 𝑓 (𝑥) is a constant function.
See also:
is_linear_structure(), linear_structures().
123
Sage 9.1 Reference Manual: Cryptography, Release 9.1
EXAMPLES:
is_balanced()
Return True if the function takes the value True half of the time.
EXAMPLES:
is_bent()
Return True if the function is bent.
EXAMPLES:
is_linear_structure(val)
Return True if val is a linear structure of this Boolean function.
INPUT:
• val – either an integer or a tuple/list of F2 elements of length equal to the number of variables
See also:
has_linear_structure(), linear_structures().
EXAMPLES:
is_plateaued()
Return True if this function is plateaued, i.e. its Walsh transform takes at most three values 0 and ±𝜆,
where 𝜆 is some positive integer.
EXAMPLES:
is_symmetric()
Return True if the function is symmetric, i.e. invariant under permutation of its input bits. Another way to
see it is that the output depends only on the Hamming weight of the input.
EXAMPLES:
linear_structures()
Return all linear structures of this Boolean function as a vector subspace of F𝑛2 .
See also:
is_linear_structure(), has_linear_structure().
EXAMPLES:
125
Sage 9.1 Reference Manual: Cryptography, Release 9.1
nonlinearity()
Return the nonlinearity of the function. This is the distance to the linear functions, or the number of output
ones need to change to obtain a linear function.
EXAMPLES:
nvariables()
The number of variables of this function.
EXAMPLES:
resiliency_order()
Return the maximum value 𝑚 such that the function is resilient of order 𝑚.
A Boolean function is said to be resilient of order 𝑚 if it is balanced and correlation immune of order 𝑚.
If the function is not balanced, we return -1.
EXAMPLES:
sage: B.resiliency_order()
3
sum_of_square_indicator()
Return the sum of square indicator of the function.
EXAMPLES:
truth_table(format=’bin’)
The truth table of the Boolean function.
INPUT: a string representing the desired format, can be either
• ‘bin’ (default) : we return a tuple of Boolean values
• ‘int’ : we return a tuple of 0 or 1 values
• ‘hex’ : we return a string representing the truth_table in hexadecimal
EXAMPLES:
sage: BooleanFunction('00ab').truth_table(format='hex')
'00ab'
sage: H = '0abbacadabbacad0'
sage: len(H)
16
sage: T = BooleanFunction(H).truth_table(format='hex')
sage: T == H
True
sage: H = H * 4
sage: T = BooleanFunction(H).truth_table(format='hex')
sage: T == H
True
sage: H = H * 4
sage: T = BooleanFunction(H).truth_table(format='hex')
sage: T == H
True
sage: len(T)
256
sage: B.truth_table(format='oct')
Traceback (most recent call last):
...
ValueError: unknown output format
walsh_hadamard_transform()
Compute the Walsh Hadamard transform 𝑊 of the function 𝑓 .
∑︁
𝑊 (𝑗) = (−1)𝑓 (𝑖)⊕𝑖·𝑗
𝑖∈{0,1}𝑛
EXAMPLES:
127
Sage 9.1 Reference Manual: Cryptography, Release 9.1
class sage.crypto.boolean_function.BooleanFunctionIterator
Bases: object
Iterator through the values of a Boolean function.
EXAMPLES:
sage.crypto.boolean_function.random_boolean_function(n)
Return a random Boolean function with 𝑛 variables.
EXAMPLES:
sage.crypto.boolean_function.unpickle_BooleanFunction(bool_list)
Specific function to unpickle Boolean functions.
EXAMPLES:
FIFTEEN
Note that by default bits are interpreted in big endian order. This is not consistent with the rest of Sage, which
has a strong bias towards little endian, but is consistent with most cryptographic literature:
sage: S([0,0,0,1])
[0, 1, 0, 1]
Now we construct an SBox object for the 4-bit small scale AES S-Box (cf. sage.crypto.mq.sr):
AUTHORS:
• Rusydi H. Makarim (2016-03-31) : added more functions to determine related cryptographic properties
• Yann Laigle-Chapuy (2009-07-01): improve linear and difference matrix computation
• Martin R. Albrecht (2008-03-12): initial implementation
129
Sage 9.1 Reference Manual: Cryptography, Release 9.1
REFERENCES:
• [He2002]
• [BKLPPRSV2007]
• [CDL2015]
autocorrelation_table()
Return the autocorrelation table corresponding to this S-Box.
for an 𝑚 × 𝑛 S-Box 𝑆, its autocorrelation table entry at row 𝑎 ∈ F𝑚 𝑛
2 and column 𝑏 ∈ F2 (considering
their integer representation) is defined as:
∑︁
(−1)𝑏·𝑆(𝑥)⊕𝑏·𝑆(𝑥⊕𝑎)
𝑥∈F𝑚
2
Equivalently, the columns 𝑏 of autocorrelation table correspond to the autocorrelation spectrum of compo-
nent function 𝑏 · 𝑆(𝑥).
EXAMPLES:
boomerang_connectivity_table()
Return the boomerang connectivity table (BCT) for this S-Box.
Boomerang connectivity matrix of an invertible 𝑚 × 𝑚 S-Box 𝑆 is an 2𝑚 × 2𝑚 matrix with entry at row
∆ 𝑖 ∈ F𝑚 𝑚
2 and column ∆𝑜 ∈ F2 equal to
−1
|{𝑥 ∈ F𝑚
2 |𝑆 (𝑆(𝑥) ⊕ ∆𝑜 ) ⊕ 𝑆 −1 (𝑆(𝑥 ⊕ ∆𝑖 ) ⊕ ∆𝑜 ) = ∆𝑖 }|.
For more results concerning boomerang connectivity matrix, see [CHPSS18] . The algorithm used here, is
the one from Dunkelman, published in a preprint, see [Du2018] .
EXAMPLES:
boomerang_uniformity()
Return the boomerang uniformity
The boomerang uniformity is defined as the highest entry in the boomerang connectivity table, ignoring
the first row and column.
EXAMPLES:
This output completely describes the S-Box. For instance, we can check that S([0,1]) -> [1,0]
satisfies every clause if the first input bit corresponds to the index 1 and the last output bit corresponds to
the index 3 in the output.
131
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: print(S.cnf(format='dimacs'))
p cnf 4 8
1 2 -3 0
1 2 4 0
1 -2 3 0
1 -2 -4 0
-1 2 -3 0
-1 2 -4 0
-1 -2 3 0
-1 -2 4 0
sage: print(S.cnf(format='dimacs_headless'))
1 2 -3 0
1 2 4 0
1 -2 3 0
1 -2 -4 0
-1 2 -3 0
-1 2 -4 0
-1 -2 3 0
-1 -2 4 0
This might be helpful in combination with the xi and yi parameter to assign indices manually:
derivative(u)
Return the derivative in direction of u
INPUT:
• u – either an integer or a tuple/list of F2 elements of length equal to m
The derivative of 𝐹 in direction of 𝑢 is defined as 𝑥 ↦→ 𝐹 (𝑥) + 𝐹 (𝑥 + 𝑢).
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: s = SBox(0,1,2,3)
sage: s.derivative(1)
(1, 1, 1, 1)
sage: u = [1,0]
sage: s.derivative(u)
(1, 1, 1, 1)
sage: v = vector(GF(2), [1,0])
sage: s.derivative(v)
(1, 1, 1, 1)
sage: s.derivative(4)
Traceback (most recent call last):
...
(continues on next page)
133
Sage 9.1 Reference Manual: Cryptography, Release 9.1
difference_distribution_table()
Return difference distribution table (DDT) A for this S-box.
The rows of A encode the differences Delta I of the input and the columns encode the difference Delta
O for the output. The bits are ordered according to the endianess of this S-box. The value at A[Delta
I,Delta O] encodes how often Delta O is the actual output difference given Delta I as input
difference.
See [He2002] for an introduction to differential cryptanalysis.
EXAMPLES:
differential_branch_number()
Return differential branch number of this S-Box.
The differential branch number of an S-Box 𝑆 is defined as
fixed_points()
Return a list of all fixed points of this S-Box.
EXAMPLES:
from_bits(x, n=None)
Return integer for bitstring x of length n.
INPUT:
• x - a bitstring
• n - bit length (optional)
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox(7,6,0,4,2,5,1,3)
sage: S.from_bits( [1,1,0])
6
has_linear_structure()
Return True if there exists a nonzero component function of this S-Box that has a linear structure.
See also:
is_linear_structure(), linear_structures().
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox(12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2)
sage: S.has_linear_structure()
True
input_size()
Return the input size of this S-Box.
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox([0, 3, 2, 1, 1, 3, 2, 0])
sage: S.input_size()
3
interpolation_polynomial(k=None)
Return a univariate polynomial over an extension field representing this S-box.
If m is the input length of this S-box then the extension field is of degree m.
If the output length does not match the input length then a TypeError is raised.
INPUT:
• k - an instance of F2𝑚 (default: None)
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox(7,6,0,4,2,5,1,3)
sage: f = S.interpolation_polynomial()
sage: f
x^6 + a*x^5 + (a + 1)*x^4 + (a^2 + a + 1)*x^3
+ (a^2 + 1)*x^2 + (a + 1)*x + a^2 + a + 1
sage: a = f.base_ring().gen()
135
Sage 9.1 Reference Manual: Cryptography, Release 9.1
inverse()
Return the inverse of this S-Box.
Note that the S-Box must be invertible, otherwise it will raise a TypeError.
EXAMPLES:
is_almost_bent()
Return True if this S-Box is an almost bent (AB) function.
An 𝑚 × 𝑚 S-Box 𝑆, for 𝑚 odd, is called almost bent if its nonlinearity is equal to 2𝑚−1 − 2(𝑚−1)/2 .
EXAMPLES:
is_apn()
Return True if this S-Box is an almost perfect nonlinear (APN) function.
An 𝑚 × 𝑚 S-Box 𝑆 is called almost perfect nonlinear if for every nonzero 𝛼 ∈ F𝑚 2 and every 𝛽 ∈ F2 ,
𝑚
the equation 𝑆(𝑥) ⊕ 𝑆(𝑥 ⊕ 𝛼) = 𝛽 has 0 or 2 solutions. Equivalently, the differential uniformity of 𝑆 is
equal to 2.
EXAMPLES:
is_balanced()
Return True if this S-Box is balanced.
An S-Box is balanced if all its component functions are balanced.
EXAMPLES:
is_bent()
Return True if this S-Box is bent, i.e. its nonlinearity is equal to 2𝑚−1 − 2𝑚/2−1 where 𝑚 is the input
size of the S-Box.
EXAMPLES:
is_involution()
Return True if this S-Box is an involution, i.e. the inverse S-Box is equal itself.
EXAMPLES:
is_linear_structure(a, b)
Return True if 𝑎 is a linear structure of the component function 𝑏 · 𝑆(𝑥) where S is this 𝑚 × 𝑛 S-Box.
INPUT:
• a – either an integer or a tuple of F2 elements of length equal to the input size of SBox
• b – either an integer or a tuple of F2 elements of length equal to the output size of SBox
See also:
linear_structures(), has_linear_structure().
EXAMPLES:
137
Sage 9.1 Reference Manual: Cryptography, Release 9.1
is_monomial_function()
Return True if this S-Box is a monomial/power function.
EXAMPLES:
sage: S = SBox(0,1,5,6,7,2,3,4)
sage: S.is_monomial_function()
True
sage: S.interpolation_polynomial()
x^6
is_permutation()
Return True if this S-Box is a permutation.
EXAMPLES:
sage: S = SBox(3,2,0,0,2,1,1,3)
sage: S.is_permutation()
False
is_plateaued()
Return True if this S-Box is plateaued, i.e. for all nonzero 𝑏 ∈ F𝑛2 the Boolean function 𝑏 · 𝑆(𝑥) is
plateaued.
EXAMPLES:
linear_approximation_table(scale=’absolute_bias’)
Return linear approximation table (LAT) 𝐴 for this S-box.
The entry 𝐴[𝛼, 𝛽] corresponds to the probability 𝑃 𝑟[𝛼 · 𝑥 = 𝛽 · 𝑆(𝑥)], where 𝑆 is this S-box mapping
𝑛-bit inputs to 𝑚-bit outputs. There are three typical notations for this probability used in the literature:
• 𝑃 𝑟[𝛼 · 𝑥 = 𝛽 · 𝑆(𝑥)] = 1/2 + 𝑒(𝛼, 𝛽), where 𝑒(𝛼, 𝛽) is called the bias,
• 2 · 𝑃 𝑟[𝛼 · 𝑥 = 𝛽 · 𝑆(𝑥)] = 1 + 𝑐(𝛼, 𝛽), where 𝑐(𝛼, 𝛽) = 2 · 𝑒(𝛼, 𝛽) is the correlation, and
ˆ 𝛽), where 𝑆(𝛼,
• 2(𝑚+1) · 𝑃 𝑟[𝛼 · 𝑥 = 𝛽 · 𝑆(𝑥)] = 2𝑚 + 𝑆(𝛼, ˆ 𝛽) is the Fourier coefficient of S.
True
True
According to this table the first bit of the input is equal to the third bit of the output 6 out of 8 times:
linear_branch_number()
Return linear branch number of this S-Box.
139
Sage 9.1 Reference Manual: Cryptography, Release 9.1
where LAM(𝛼, 𝛽) is the entry at row 𝛼 and column 𝛽 of linear approximation matrix correspond to this
S-Box. The wt(𝑥) denotes the Hamming weight of 𝑥.
EXAMPLES:
linear_structures()
Return a list of 3-valued tuple (𝑏, 𝛼, 𝑐) such that 𝛼 is a 𝑐-linear structure of the component function 𝑏·𝑆(𝑥).
A Boolean function 𝑓 : F𝑚 2 ↦→ F2 is said to have a 𝑐-linear structure if there exists a nonzero 𝛼 such that
𝑓 (𝑥) ⊕ 𝑓 (𝑥 ⊕ 𝛼) is a constant function 𝑐.
An 𝑚 × 𝑛 S-Box 𝑆 has a linear structure if there exists a component function 𝑏 · 𝑆(𝑥) that has a linear
structure.
The three valued tuple (𝑏, 𝛼, 𝑐) shows that 𝛼 is a 𝑐-linear structure of the component function 𝑏 · 𝑆(𝑥). This
implies that for all output differences 𝛽 of the S-Box correspond to input difference 𝛼, we have 𝑏 · 𝛽 = 𝑐.
See also:
is_linear_structure(), has_linear_structure().
EXAMPLES:
linearity()
Return the linearity of this S-Box.
EXAMPLES:
max_degree()
Return the maximal algebraic degree of all its component functions.
EXAMPLES:
maximal_difference_probability()
Return the difference probability of the difference with the highest probability in the range between 0.0
and 1.0 indicating 0% or 100% respectively.
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox(7,6,0,4,2,5,1,3)
sage: S.maximal_difference_probability()
0.25
maximal_difference_probability_absolute()
Return the difference probability of the difference with the highest probability in absolute terms, i.e. how
often it occurs in total.
Equivalently, this is equal to the differential uniformity of this S-Box.
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox(7,6,0,4,2,5,1,3)
sage: S.maximal_difference_probability_absolute()
2
maximal_linear_bias_absolute()
Return maximal linear bias, i.e. how often the linear approximation with the highest bias is true or false
minus 2𝑛−1 .
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox(7,6,0,4,2,5,1,3)
sage: S.maximal_linear_bias_absolute()
2
maximal_linear_bias_relative()
Return maximal bias of all linear approximations of this S-box.
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox(7,6,0,4,2,5,1,3)
sage: S.maximal_linear_bias_relative()
0.25
min_degree()
Return the minimal algebraic degree of all its component functions.
EXAMPLES:
sage: from sage.crypto.sbox import SBox
sage: S = SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2])
sage: S.min_degree()
2
nonlinearity()
Return the nonlinearity of this S-Box.
The nonlinearity of an S-Box is defined as the minimum nonlinearity of all its component functions.
EXAMPLES:
141
Sage 9.1 Reference Manual: Cryptography, Release 9.1
output_size()
Return the output size of this S-Box.
EXAMPLES:
sage: S.polynomials()
[x0*x2 + x1 + y1 + 1,
x0*x1 + x1 + x2 + y0 + y1 + y2 + 1,
x0*y1 + x0 + x2 + y0 + y2,
x0*y0 + x0*y2 + x1 + x2 + y0 + y1 + y2 + 1,
x1*x2 + x0 + x1 + x2 + y2 + 1,
x0*y0 + x1*y0 + x0 + x2 + y1 + y2,
x0*y0 + x1*y1 + x1 + y1 + 1,
x1*y2 + x1 + x2 + y0 + y1 + y2 + 1,
x0*y0 + x2*y0 + x1 + x2 + y1 + 1,
x2*y1 + x0 + y1 + y2,
x2*y2 + x1 + y1 + 1,
y0*y1 + x0 + x2 + y0 + y1 + y2,
y0*y2 + x1 + x2 + y0 + y1 + 1,
y1*y2 + x2 + y0]
We can get a direct representation by computing a lexicographical Groebner basis with respect to the right
variable ordering, i.e. a variable ordering where the output bits are greater than the input bits:
ring()
Create, return and cache a polynomial ring for S-box polynomials.
EXAMPLES:
solutions(X=None, Y=None)
Return a dictionary of solutions to this S-box.
INPUT:
• X - input variables (default: None)
• Y - output variables (default: None)
EXAMPLES:
to_bits(x, n=None)
Return bitstring of length n for integer x. The returned bitstring is guaranteed to have length n.
INPUT:
• x - an integer
• n - bit length (optional)
EXAMPLES:
sage: S( S.to_bits( 6 ) )
[0, 0, 1]
sage.crypto.sbox.feistel_construction(*args)
Return an S-Box constructed by Feistel structure using smaller S-Boxes in args. The number of round in the
construction is equal to the number of S-Boxes provided as input. For more results concerning the differential
uniformity and the nonlinearity of S-Boxes constructed by Feistel structures see [CDL2015] .
143
Sage 9.1 Reference Manual: Cryptography, Release 9.1
INPUT:
• args - a finite iterable SBox objects
EXAMPLES:
Suppose we construct an 8 × 8 S-Box with 3-round Feistel construction from the S-Box of PRESENT:
sage: S.nonlinearity()
96
sage: S.differential_branch_number()
2
sage: S.linear_branch_number()
2
sage.crypto.sbox.misty_construction(*args)
Return an S-Box constructed by MISTY structure using smaller S-Boxes in args. The number of round in the
construction is equal to the number of S-Boxes provided as input. For further result related to the nonlinearity
and differential uniformity of the constructed S-Box one may consult [CDL2015].
INPUT:
• args - a finite iterable SBox objects
EXAMPLES:
We construct an 8 × 8 S-Box using 3-round MISTY structure with the following 4 × 4 S-Boxes 𝑆1, 𝑆2, 𝑆3 (see
Example 2 in [CDL2015]):
SIXTEEN
145
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• Fantomas ([GLSV2014])
• FLY ([KG2016])
• Fox ([VJ2004])
• Iceberg ([SPRQL2004])
• Iraqi (Wikipedia article Iraqi_block_cipher)
• iScream ([GLSVJGK2014])
• Kalyna_pi0, . . . , Kalyna_pi3 ([OGKRKGBDDP2015])
• Khazad ([BR2000b])
• Kuznyechik (Kuznechik, Streebog, Stribog) ([Fed2015])
• Lilliput-AE ([ABCFHLLMRT2019])
• MD2 ([Kal1992])
• newDES ([Sco1985])
• Picaro ([PRC2012])
• Safer ([Mas1994])
• Scream ([CDL2015],[GLSVJGK2014]_)
• SEED_S0, SEED_S1 ([LLYCL2005])
• SKINNY_8 (ForkSkinny_8 [ALPRRV2019], Remus_8 [IKMP2019A], Romulus [IKMP2019B])
([BJKLMPSSS2016])
• Skipjack ([U.S1998])
• SNOW_3G_sq ([ETS2006a])
• SMS4 ([Ltd06])
• Turing ([RH2003b])
• Twofish_p0, Twofish_p1 ([SKWWHF1998])
• Whirlpool ([BR2000c])
• Zorro ([GGNS2013])
• ZUC_S0, ZUC_S1 ([ETS2011])
7 bit to 7 bit
• Wage ([AAGMRZ2019])
6 bit to 6 bit
• Fides_6 ([BBKMW2013])
• APN_6 ([BDMW2010])
• SC2000_6 ([SYYTIYTT2002])
5 bit to 5 bit
• Ascon (ISAP [DEMMMPU2019]) ([DEMS2016])
• DryGASCON128 ([Rio2019])
• Fides_5 ([BBKMW2013])
• SC2000_5 ([SYYTIYTT2002])
• Shamash ([PM2019])
• SYCON ([SMS2019])
4 bit to 4 bit
• Elephant ([BCDM2019])
• KNOT ([ZDYBXJZ2019])
• Pyjamask_4 ([GJKPRSS2019])
• SATURNIN_0, SATURNIN_1 ([CDLNPPS2019])
• Spook (Clyde, Shadow) ([BBBCDGLLLMPPSW2019])
• TRIFLE ([DGMPPS2019])
• Yarara, Coral ([MP2019])
• DES_S1_1, . . . , DES_S1_4, . . . , DES_S8_4 ([U.S1999])
• Lucifer_S0, Lucifer_S1 ([Sor1984])
• GOST_1, . . . , GOST_8 (http://www.cypherpunks.ru/pygost/)
• GOST2_1, GOST2_2 (http://www.cypherpunks.ru/pygost/)
• Magma_1, . . . , Magma_8 ([Fed2015])
• GOST_IETF_1, . . . , GOST_IETF_8 (http://www.cypherpunks.ru/pygost/)
• Hummingbird_2_S1, . . . , Hummingbird_2_S4 ([ESSS2011])
• LBlock_0, . . . , LBlock_9 ([WZ2011])
• SERPENT_S0, . . . , SERPENT_S7 ([BAK1998])
• KLEIN ([GNL2011])
• MIBS ([ISSK2009)]
• Midori_Sb0 (MANTIS, CRAFT), Midori_Sb1 ([BBISHAR2015])
• Noekeon ([DPVAR2000])
• Piccolo ([SIHMAS2011])
• Panda ([YWHWXSW2014])
• PRESENT (CiliPadi [ZJRRS2019], PHOTON [BCDGNPY2019], ORANGE [CN2019])
([BKLPPRSV2007])
• GIFT (Fountain_1, HYENA [CDJN2019], TGIF [IKMPSSS2019]) ([BPPSST2017])
• Fountain_1, Fountain_2, Fountain_3, Fountain_4 ([Zha2019])
• Pride ([ADKLPY2014])
• PRINCE ([BCGKKKLNPRRTY2012])
• Prost ([KLLRSY2014])
• Qarma_sigma0, Qarma_sigma1 (Qameleon [ABBDHR2019]), Qarma_sigma2 ([Ava2017])
• REC_0 (earlier version of [ZBLRYV2015])
• Rectangle ([ZBLRYV2015])
147
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• SC2000_4 ([SYYTIYTT2002])
• SKINNY_4 (ForkSkinny_4 [ALPRRV2019], Remus_4 [IKMP2019A]) ([BJKLMPSSS2016])
• TWINE ([SMMK2013])
• Luffa_v1 ([DCSW2008])
• Luffa ([DCSW2008])
• BLAKE_1, . . . , BLAKE_9 ([AHMP2008])
• JH_S0, JH_S1 ([Wu2009])
• SMASH_256_S1, . . . , SMASH_256_S3 ([Knu2005])
• Anubis_S0, Anubis_S1 ([BR2000a])
• CLEFIA_SS0, . . . , CLEFIA_SS3 ([SSAMI2007])
• Enocoro_S4 ([WFYTP2008])
• Iceberg_S0, Iceberg_S1 ([SPRQL2004])
• Khazad_P, Khazad_Q ([BR2000b])
• Whirlpool_E, Whirlpool_R ([BR2000c])
• CS_cipher_F, CS_cipher_G ([SV2000])
• Fox_S1, . . . , Fox_S3 ([VJ2004])
• Twofish_Q0_T0, . . . , Twofish_Q0_T3, Twofish_Q1_T0, . . . , Twofish_Q1_T3 ([SKWWHF1998])
• Kuznyechik_nu0, Kuznyechik_nu1, Kuznyechik_sigma, Kuznyechik_phi ([BPU2016])
• UDCIKMP11 ([UDCIKMP2011])
• Optimal_S0, . . . , Optimal_S15 ([LP2007])
• Serpent_type_S0, . . . , Serpent_type_S19 ([LP2007])
• Golden_S0, . . . , Golden_S3 ([Saa2011])
• representatives for all 302 affine equivalence classes ([dCa2007])
3 bit to 3 bit
• SEA ([SPGQ2006])
• PRINTcipher ([KLPR2010])
• Pyjamask_3 ([GJKPRSS2019])
Additionally this modules offers a dictionary 𝑠𝑏𝑜𝑥𝑒𝑠 of all implemented above S-boxes for the purpose of easy iteration
over all available S-boxes.
EXAMPLES:
We can print the S-Boxes with differential uniformity 2:
AUTHOR:
• Leo Perrin: initial collection of sboxes
• Friedrich Wiemer (2017-05-12): refactored list for inclusion in SAGE
• Lukas Stennes (2019-06-25): added NIST LWC round 1 candidates
sage.crypto.sboxes.bracken_leander(n)
Return the Bracken-Leander construction.
2𝑘
+2𝑘 +1
For n = 4*k and odd k, the construction is 𝑥 ↦→ 𝑥2 over F2𝑛
INPUT:
• n – size of the S-Box
EXAMPLES:
sage: from sage.crypto.sboxes import bracken_leander
sage: sbox = bracken_leander(12); [sbox(i) for i in range(8)]
[0, 1, 2742, 4035, 1264, 408, 1473, 1327]
sage.crypto.sboxes.gold(n, i)
𝑖
Return the Gold function defined by 𝑥 ↦→ 𝑥2 +1 over F2𝑛 .
INPUT:
• n – size of the S-Box
• i – a positive integer
EXAMPLES:
sage: from sage.crypto.sboxes import gold
sage: gold(3, 1)
(0, 1, 3, 4, 5, 6, 7, 2)
sage: gold(3, 1).differential_uniformity()
(continues on next page)
149
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage.crypto.sboxes.kasami(n, i)
2𝑖 𝑖
Return the Kasami function defined by 𝑥 ↦→ 𝑥2 −2 +1 over F2𝑛 .
INPUT:
• n – size of the S-Box
• i – a positive integer
EXAMPLES:
sage: from sage.crypto.sboxes import kasami
sage: kasami(3, 1)
(0, 1, 3, 4, 5, 6, 7, 2)
sage: from sage.crypto.sboxes import gold
sage: kasami(3, 1) == gold(3, 1)
True
sage: kasami(4, 2)
(0, 1, 13, 11, 14, 9, 6, 7, 10, 4, 15, 2, 8, 3, 5, 12)
sage: kasami(4, 2) != gold(4, 2)
True
sage.crypto.sboxes.monomial_function(n, e)
Return an S-Box as a function 𝑥𝑒 defined over F2𝑛 .
INPUT:
• n – size of the S-Box (i.e. the degree of the finite field extension)
• e – exponent of the monomial function
EXAMPLES:
sage: from sage.crypto.sboxes import monomial_function
sage: S = monomial_function(7, 3)
sage: S.differential_uniformity()
2
sage: S.input_size()
7
sage: S.is_permutation()
True
sage.crypto.sboxes.niho(n)
Return the Niho function over F2𝑛 .
𝑡
+2𝑠 −1
It is defined by 𝑥 ↦→ 𝑥2 with 𝑠 = 𝑡/2 if t is even or 𝑠 = (3𝑡 + 1)/2 if t is odd.
INPUT:
• n – size of the S-Box
EXAMPLES:
sage: from sage.crypto.sboxes import niho
sage: niho(3)
(0, 1, 7, 2, 3, 4, 5, 6)
sage.crypto.sboxes.v(n)
(𝑛−1)/2
Return the Welch function defined by 𝑥 ↦→ 𝑥2 +3
over F2𝑛 .
INPUT:
• n – size of the S-Box
EXAMPLES:
sage.crypto.sboxes.welch(n)
(𝑛−1)/2
Return the Welch function defined by 𝑥 ↦→ 𝑥2 +3
over F2𝑛 .
INPUT:
• n – size of the S-Box
EXAMPLES:
151
Sage 9.1 Reference Manual: Cryptography, Release 9.1
SEVENTEEN
polynomial_system(P=None, K=None)
Return a tuple F,s for plaintext P and key K where F is an polynomial system and s a dictionary which
maps key variables to their solutions.
INPUT: P – plaintext (vector, list) K – key (vector, list)
EXAMPLES:
random_element()
Return random element. Usually this is a list of elements in the base field of length ‘blocksize’.
EXAMPLES:
153
Sage 9.1 Reference Manual: Cryptography, Release 9.1
ring()
Return the ring in which the system is defined.
EXAMPLES:
sbox()
Return SBox object for self.
EXAMPLES:
varformatstr(name)
Return format string for a given name ‘name’ which is understood by print et al.
Such a format string is used to construct variable names. Typically those format strings are somewhat like
‘name%02d%02d’ such that rounds and offset in a block can be encoded.
INPUT: name – string
EXAMPLES:
vars(name, round)
Return a list of variables given a name ‘name’ and an index ‘round’.
INPUT: name – string round – integer index
EXAMPLES:
154 Chapter 17. Abstract base class for generators of polynomial systems
Sage 9.1 Reference Manual: Cryptography, Release 9.1
varstrs(name, round)
Return a list of variable names given a name ‘name’ and an index ‘round’.
This function is typically used by self._vars.
INPUT: name – string round – integer index
EXAMPLES:
155
Sage 9.1 Reference Manual: Cryptography, Release 9.1
156 Chapter 17. Abstract base class for generators of polynomial systems
CHAPTER
EIGHTEEN
Sage supports polynomial system generation for small scale (and full scale) AES variants over F2 and F2𝑒 . Also,
Sage supports both the specification of SR as given in the papers [CMR2005] and [CMR2006] and a variant of SR*
which is equivalent to AES.
SR is a family of parameterizable variants of the AES suitable as a framework for comparing different cryptanalytic
techniques that can be brought to bear on the AES. It is different from Mini-AES, whose purpose is as a teaching
tool to help beginners understand the basic structure and working of the full AES.
AUTHORS:
• Martin Albrecht (2008,2009-01): usability improvements
• Martin Albrecht (2007-09): initial version
• Niles Johnson (2010-08): (trac ticket #3893) random_element() should pass on *args and **kwds.
EXAMPLES:
We construct SR(1,1,1,4) and study its properties.
sage: sr = mq.SR(1, 1, 1, 4)
n is the number of rounds, r the number of rows in the state array, c the number of columns in the state array, and e
the degree of the underlying field.
sage: sr.n, sr.r, sr.c, sr.e
(1, 1, 1, 4)
157
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr.MixColumns
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
sage: sr.Lin
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
[ a a 1 a^3 + a^2 + a + 1]
[ a^3 + a a^2 a^2 1]
[ 1 a^3 a + 1 a + 1]
sage: sr.M
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
[ a a 1 a^3 + a^2 + a + 1]
[ a^3 + a a^2 a^2 1]
[ 1 a^3 a + 1 a + 1]
sage: sr.Mstar
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
[ a a 1 a^3 + a^2 + a + 1]
[ a^3 + a a^2 a^2 1]
[ 1 a^3 a + 1 a + 1]
sage: sr = mq.SR(10,4,4,8)
sage: sr.Mstar == ~sr.MixColumns * sr.M
True
We can compute a Groebner basis for the ideals spanned by SR instances to recover all solutions to the system.:
Note that the order of k000, k001, k002 and k003 is little endian. Thus the result k002 + 1, k001, k000
indicates that the key is either 𝑎 or 𝑎 + 1. We can verify that both keys encrypt P to the same ciphertext:
158 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr(P,[a])
[0]
sage: sr(P,[a+1])
[0]
All solutions can easily be recovered using the variety function for ideals.:
sage: I = F.ideal()
sage: for V in I.variety():
....: for k,v in sorted(V.items()):
....: print("{} {}".format(k, v))
....: print("\n")
k003 0
k002 1
k001 0
k000 0
s003 1
s002 0
s001 0
s000 1
w103 1
w102 1
w101 0
w100 0
x103 0
x102 1
x101 1
x100 1
k103 0
k102 0
k101 1
k100 0
k003 1
k002 1
k001 0
k000 0
s003 0
s002 1
s001 1
s000 1
w103 0
w102 1
w101 0
w100 0
x103 1
x102 0
x101 0
x100 1
k103 1
k102 0
k101 1
k100 0
We can also verify the correctness of the variety by evaluating all ideal generators on all points.:
159
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Note that the S-Box object for SR can be constructed with a call to sr.sbox():
sage: sr = mq.SR(1,1,1,4, gf2=True, polybori=True)
sage: S = sr.sbox()
160 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
161
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: S.interpolation_polynomial()
(a^2 + 1)*x^14 + x^13 + (a^3 + a^2)*x^11 + (a^2 + 1)*x^7 + a^2 + a
The SR_gf2_2 gives an example how use alternative polynomial representations of the S-Box for construction of
polynomial systems.
REFERENCES:
• [CMR2005]
• [CMR2006]
• [MR2002]
class sage.crypto.mq.sr.AllowZeroInversionsContext(sr)
Bases: object
Temporarily allow zero inversion.
sage.crypto.mq.sr.SR(n=1, r=1, c=1, e=4, star=False, **kwargs)
Return a small scale variant of the AES polynomial system constructor subject to the following conditions:
INPUT:
• n - the number of rounds (default: 1)
• r - the number of rows in the state array (default: 1)
• c - the number of columns in the state array (default: 1)
• e - the exponent of the finite extension field (default: 4)
• star - determines if SR* or SR should be constructed (default: False)
• aes_mode - as the SR key schedule specification differs slightly from the AES key schedule, this param-
eter controls which schedule to use (default: True)
• gf2 - generate polynomial systems over F2 rather than over F2𝑒 (default: False)
• polybori - use the BooleanPolynomialRing as polynomial representation (default: True, F2
only)
• order - a string to specify the term ordering of the variables (default: deglex)
• postfix - a string which is appended after the variable name (default: ‘’)
• allow_zero_inversions - a boolean to control whether zero inversions raise an exception (default:
False)
• correct_only - only include correct inversion polynomials (default: False, F2 only)
• biaffine_only - only include bilinear and biaffine inversion polynomials (default: True, F2 only)
EXAMPLES:
sage: sr = mq.SR(1, 1, 1, 4)
sage: ShiftRows = sr.shift_rows_matrix()
sage: MixColumns = sr.mix_columns_matrix()
sage: Lin = sr.lin_matrix()
sage: M = MixColumns * ShiftRows * Lin
sage: print(sr.hex_str_matrix(M))
5 1 C 5
2 2 1 F
(continues on next page)
162 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr = mq.SR(1, 2, 1, 4)
sage: ShiftRows = sr.shift_rows_matrix()
sage: MixColumns = sr.mix_columns_matrix()
sage: Lin = sr.lin_matrix()
sage: M = MixColumns * ShiftRows * Lin
sage: print(sr.hex_str_matrix(M))
F 3 7 F A 2 B A
A A 5 6 8 8 4 9
7 8 8 2 D C C 3
4 6 C C 5 E F F
A 2 B A F 3 7 F
8 8 4 9 A A 5 6
D C C 3 7 8 8 2
5 E F F 4 6 C C
sage: sr = mq.SR(1, 2, 2, 4)
sage: ShiftRows = sr.shift_rows_matrix()
sage: MixColumns = sr.mix_columns_matrix()
sage: Lin = sr.lin_matrix()
sage: M = MixColumns * ShiftRows * Lin
sage: print(sr.hex_str_matrix(M))
F 3 7 F 0 0 0 0 0 0 0 0 A 2 B A
A A 5 6 0 0 0 0 0 0 0 0 8 8 4 9
7 8 8 2 0 0 0 0 0 0 0 0 D C C 3
4 6 C C 0 0 0 0 0 0 0 0 5 E F F
A 2 B A 0 0 0 0 0 0 0 0 F 3 7 F
8 8 4 9 0 0 0 0 0 0 0 0 A A 5 6
D C C 3 0 0 0 0 0 0 0 0 7 8 8 2
5 E F F 0 0 0 0 0 0 0 0 4 6 C C
0 0 0 0 A 2 B A F 3 7 F 0 0 0 0
0 0 0 0 8 8 4 9 A A 5 6 0 0 0 0
0 0 0 0 D C C 3 7 8 8 2 0 0 0 0
0 0 0 0 5 E F F 4 6 C C 0 0 0 0
0 0 0 0 F 3 7 F A 2 B A 0 0 0 0
0 0 0 0 A A 5 6 8 8 4 9 0 0 0 0
0 0 0 0 7 8 8 2 D C C 3 0 0 0 0
0 0 0 0 4 6 C C 5 E F F 0 0 0 0
sage: sr = mq.SR(1, 1, 1, 4)
sage: ShiftRows = sr.shift_rows_matrix()
sage: MixColumns = sr.mix_columns_matrix()
sage: Lin = sr.lin_matrix()
sage: M = MixColumns * ShiftRows * Lin
sage: print(sr.hex_str_matrix(M))
5 1 C 5
2 2 1 F
(continues on next page)
163
Sage 9.1 Reference Manual: Cryptography, Release 9.1
add_round_key(d, key)
Perform the AddRoundKey operation on d using key.
INPUT:
• d - state array or something coercible to a state array
• key - state array or something coercible to a state array
EXAMPLES:
sage: sr = mq.SR(10, 4, 4, 4)
sage: D = sr.random_state_array()
sage: K = sr.random_state_array()
sage: sr.add_round_key(D, K) == K + D
True
base_ring()
Return the base field of self as determined by self.e.
EXAMPLES:
sage: sr = mq.SR(10, 2, 2, 4)
sage: sr.base_ring().polynomial()
a^4 + a + 1
block_order()
Return a block order for self where each round is a block.
EXAMPLES:
sage: sr = mq.SR(2, 1, 1, 4)
sage: sr.block_order()
Block term order with blocks:
(Degree lexicographic term order of length 16,
Degree lexicographic term order of length 16,
Degree lexicographic term order of length 4)
sage: P = sr.ring(order='block')
sage: print(P.repr_long())
Polynomial Ring
Base Ring : Finite Field in a of size 2^4
Size : 36 Variables
Block 0 : Ordering : deglex
Names : k200, k201, k202, k203, x200, x201, x202, x203, w200,
˓→ w201, w202, w203, s100, s101, s102, s103
164 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
hex_str(M, typ=’matrix’)
Return a hex string for the provided AES state array/matrix.
INPUT:
• M - state array
• typ - controls what to return, either ‘matrix’ or ‘vector’ (default: 'matrix')
EXAMPLES:
sage: sr = mq.SR(2, 2, 2, 4)
sage: k = sr.base_ring()
sage: A = matrix(k, 2, 2, [1, k.gen(), 0, k.gen()^2])
sage: sr.hex_str(A)
' 1 2 \n 0 4 \n'
hex_str_matrix(M)
Return a two-dimensional AES-like representation of the matrix M.
That is, show the finite field elements as hex strings.
INPUT:
• M - an AES state array
EXAMPLES:
sage: sr = mq.SR(2, 2, 2, 4)
sage: k = sr.base_ring()
sage: A = matrix(k, 2, 2, [1, k.gen(), 0, k.gen()^2])
sage: sr.hex_str_matrix(A)
' 1 2 \n 0 4 \n'
hex_str_vector(M)
Return a one-dimensional AES-like representation of the matrix M.
That is, show the finite field elements as hex strings.
INPUT:
• M - an AES state array
EXAMPLES:
sage: sr = mq.SR(2, 2, 2, 4)
sage: k = sr.base_ring()
sage: A = matrix(k, 2, 2, [1, k.gen(), 0, k.gen()^2])
sage: sr.hex_str_vector(A)
'1024'
is_state_array(d)
Return True if d is a state array, i.e. has the correct dimensions and base field.
EXAMPLES:
165
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr = mq.SR(2, 2, 4, 8)
sage: k = sr.base_ring()
sage: sr.is_state_array( matrix(k, 2, 4) )
True
sage: sr = mq.SR(2, 2, 4, 8)
sage: k = sr.base_ring()
sage: sr.is_state_array( matrix(k, 4, 4) )
False
key_schedule(kj, i)
Return 𝑘𝑖 for a given 𝑖 and 𝑘𝑗 with 𝑗 = 𝑖 − 1.
EXAMPLES:
key_schedule_polynomials(i)
Return polynomials for the 𝑖-th round of the key schedule.
INPUT:
• i - round (0 ≤ 𝑖 ≤ 𝑛)
EXAMPLES:
The 0-th subkey is the user provided key, so only conjugacy relations or field polynomials are added.:
sage: sr.key_schedule_polynomials(0)
(k000^2 + k000, k001^2 + k001, k002^2 + k002, k003^2 + k003)
The 1-th subkey is derived from the user provided key according to the key schedule which is non-linear.:
sage: sr.key_schedule_polynomials(1)
(k100 + s000 + s002 + s003,
k101 + s000 + s001 + s003 + 1,
k102 + s000 + s001 + s002 + 1,
k103 + s001 + s002 + s003 + 1,
k100^2 + k100, k101^2 + k101, k102^2 + k102, k103^2 + k103,
s000^2 + s000, s001^2 + s001, s002^2 + s002, s003^2 + s003,
s000*k000 + s000*k003 + s001*k002 + s002*k001 + s003*k000,
s000*k000 + s000*k001 + s001*k000 + s001*k003 + s002*k002 + s003*k001,
s000*k001 + s000*k002 + s001*k000 + s001*k001 + s002*k000 + s002*k003 +
˓→s003*k002,
166 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
mix_columns(d)
Perform the MixColumns operation on d.
INPUT:
• d - state array or something coercible to a state array
EXAMPLES:
sage: sr = mq.SR(10, 4, 4, 4)
sage: E = sr.state_array() + 1; E
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
sage: sr.mix_columns(E)
[ a a + 1 1 1]
[ 1 a a + 1 1]
[ 1 1 a a + 1]
[a + 1 1 1 a]
new_generator(**kwds)
Return a new SR instance equal to this instance except for the parameters passed explicitly to this function.
INPUT:
• **kwds - see the SR constructor for accepted parameters
EXAMPLES:
sage: sr = mq.SR(2,1,1,4); sr
SR(2,1,1,4)
sage: sr.ring().base_ring()
Finite Field in a of size 2^4
167
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: F
Polynomial Sequence with 36 Polynomials in 20 Variables
and a solution:
This solution is not the only solution that we can learn from the Groebner basis of the system.
sage: F.groebner_basis()[-3:]
[k000 + 1, k001, k003 + 1]
sage: len(F.ideal().variety())
2
sage: C = sr(P,K)
sage: F,s = sr.polynomial_system(P=P, C=C)
sage: F
Polynomial Sequence with 36 Polynomials in 20 Variables
Alternatively, we can use symbols for the P and C. First, we have to create a polynomial ring:
168 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: C = sr.vars("C",0); C
(C000, C001, C002, C003)
sage: P = sr.vars("P",0)
sage: F,s = sr.polynomial_system(P=P,C=C)
sage: [(k,v) for k,v in sorted(s.items())] # this can be ignored
[(k003, 1), (k002, 1), (k001, 0), (k000, 1)]
sage: F
Polynomial Sequence with 36 Polynomials in 28 Variables
sage: F.part(0)
(P000 + w100 + k000, P001 + w101 + k001, P002 + w102 + k002, P003 + w103 +
˓→k003)
sage: F.part(-2)
(k100 + x100 + x102 + x103 + C000, k101 + x100 + x101 + x103 + C001 + 1, ...)
sage: sr = mq.SR()
sage: sr.random_element()
[ a^2]
[ a + 1]
[a^2 + 1]
[ a]
sage: sr.random_element('state_array')
[a^3 + a + 1]
sage: sr.random_element(density=0)
[0]
[0]
[0]
[0]
random_state_array(*args, **kwds)
Return a random element in MatrixSpace(self.base_ring(), self.r, self.c).
EXAMPLES:
sage: sr = mq.SR(2, 2, 2, 4)
sage: sr.random_state_array()
[ a^2 a^3 + a + 1]
[a^3 + a^2 + a + 1 a + 1]
random_vector(*args, **kwds)
Return a random vector as it might appear in the algebraic expression of self.
169
Sage 9.1 Reference Manual: Cryptography, Release 9.1
EXAMPLES:
sage: sr = mq.SR(2, 2, 2, 4)
sage: sr.random_vector()
[ a^2]
[ a + 1]
[ a^2 + 1]
[ a]
[a^3 + a^2 + a + 1]
[ a^3 + a]
[ a^3]
[ a^3 + a^2]
[ a^3 + a + 1]
[ a^3 + 1]
[ a^3 + a^2 + 1]
[ a^3 + a^2 + a]
[ a + 1]
[ a^2 + 1]
[ a]
[ a^2]
ring(order=None, reverse_variables=None)
Construct a ring as a base ring for the polynomial system.
By default, variables are ordered in the reverse of their natural ordering, i.e. the reverse of as they appear.
INPUT:
• order - a monomial ordering (default: None)
• reverse_variables - reverse rounds of variables (default: True)
The variable assignment is as follows:
• 𝑘𝑖,𝑗,𝑙 - subkey round 𝑖 word 𝑗 conjugate/bit 𝑙
• 𝑠𝑖,𝑗,𝑙 - subkey inverse round 𝑖 word 𝑗 conjugate/bit 𝑙
• 𝑤𝑖,𝑗,𝑙 - inversion input round 𝑖 word 𝑗 conjugate/bit 𝑙
• 𝑥𝑖,𝑗,𝑙 - inversion output round 𝑖 word 𝑗 conjugate/bit 𝑙
Note that the variables are ordered in column major ordering in the state array and that the bits are ordered
in little endian ordering.
For example, if 𝑥0,1,0 is a variable over F2 for 𝑟 = 2 and 𝑐 = 2 then refers to the most significant bit of
the entry in the position (1,0) in the state array matrix.
EXAMPLES:
sage: sr = mq.SR(2, 1, 1, 4)
sage: P = sr.ring(order='block')
sage: print(P.repr_long())
Polynomial Ring
Base Ring : Finite Field in a of size 2^4
Size : 36 Variables
Block 0 : Ordering : deglex
Names : k200, k201, k202, k203, x200, x201, x202, x203, w200,
˓→ w201, w202, w203, s100, s101, s102, s103
(continues on next page)
170 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr = mq.SR(1, 1, 1, 4)
sage: k = sr.base_ring()
sage: p = [k.random_element() for _ in range(sr.r*sr.c)]
sage: sr.round_polynomials(0, plaintext=p)
(w100 + k000 + (a^2 + 1), w101 + k001 + (a), w102 + k002 + (a^2), w103 + k003
˓→+ (a + 1))
sbox(inversion_only=False)
Return an S-Box object for this SR instance.
INPUT:
• inversion_only - do not include the F2 affine map when computing the S-Box (default: False)
EXAMPLES:
sage: sr.sub_byte(0)
a^2 + a
sage: sage_eval(str(sr.sub_byte(0)), {'a':2})
6
sage: S(0)
6
sage: sr.sub_byte(1)
a^3 + a + 1
sage: sage_eval(str(sr.sub_byte(1)), {'a':2})
11
sage: S(1)
11
171
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr.sub_byte(0)
a^6 + a^5 + a + 1
sage: sr.sub_byte(1)
a^6 + a^5 + a^4 + a^3 + a^2
sage: S(1)
124
sage: S(0)
0
sage: S(1)
1
sage: S(sr.k.gen())
a^3 + 1
sbox_constant()
Return the S-Box constant which is added after 𝐿(𝑥−1 ) was performed. That is 0x63 if e == 8 or 0x6
if e == 4.
EXAMPLES:
172 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr = mq.SR(10, 1, 1, 8)
sage: sr.sbox_constant()
a^6 + a^5 + a + 1
shift_rows(d)
Perform the ShiftRows operation on d.
INPUT:
• d - state array or something coercible to a state array
EXAMPLES:
sage: sr = mq.SR(10, 4, 4, 4)
sage: E = sr.state_array() + 1; E
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
sage: sr.shift_rows(E)
[1 0 0 0]
[1 0 0 0]
[1 0 0 0]
[1 0 0 0]
state_array(d=None)
Convert the parameter to a state array.
INPUT:
• d - a matrix, a list, or a tuple (default: None)
EXAMPLES:
sage: sr = mq.SR(2, 2, 2, 4)
sage: k = sr.base_ring()
sage: e1 = [k.fetch_int(e) for e in range(2*2)]; e1
[0, 1, a, a + 1]
sage: e2 = sr.phi( Matrix(k, 2*2, 1, e1) )
sage: sr.state_array(e1) # note the column major ordering
[ 0 a]
[ 1 a + 1]
sage: sr.state_array(e2)
[ 0 a]
[ 1 a + 1]
sage: sr.state_array()
[0 0]
[0 0]
sub_byte(b)
Perform SubByte on a single byte/halfbyte b.
A ZeroDivision exception is raised if an attempt is made to perform an inversion on the zero element.
This can be disabled by passing allow_zero_inversion=True to the constructor. A zero inversion
can result in an inconsistent equation system.
INPUT:
173
Sage 9.1 Reference Manual: Cryptography, Release 9.1
• b - an element in self.base_ring()
EXAMPLES:
The S-Box table for F24 :
sub_bytes(d)
Perform the non-linear transform on d.
INPUT:
• d - state array or something coercible to a state array
EXAMPLES:
sage: sr = mq.SR(1, 2, 2, 4)
sage: sr.varformatstr('x')
'x%01d%01d%01d'
(continues on next page)
174 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
variable_dict()
Return a dictionary to access variables in self.R by their names.
EXAMPLES:
sage: sr = mq.SR(1,1,1,4)
sage: sr.variable_dict()
{'k000': k000,
'k001': k001,
'k002': k002,
'k003': k003,
'k100': k100,
'k101': k101,
'k102': k102,
'k103': k103,
's000': s000,
's001': s001,
's002': s002,
's003': s003,
'w100': w100,
'w101': w101,
'w102': w102,
'w103': w103,
'x100': x100,
'x101': x101,
'x102': x102,
'x103': x103}
sage: sr = mq.SR(1,1,1,4,gf2=True)
sage: sr.variable_dict()
{'k000': k000,
'k001': k001,
'k002': k002,
'k003': k003,
'k100': k100,
'k101': k101,
'k102': k102,
'k103': k103,
's000': s000,
's001': s001,
's002': s002,
's003': s003,
'w100': w100,
'w101': w101,
'w102': w102,
'w103': w103,
'x100': x100,
'x101': x101,
'x102': x102,
'x103': x103}
175
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr = mq.SR(10, 1, 2, 4)
sage: sr.vars('x', 2)
(x200, x201, x202, x203, x210, x211, x212, x213)
sage: sr = mq.SR(10, 1, 2, 4)
sage: sr.varstr('x', 2, 1, 1)
'x211'
sage: sr = mq.SR(10, 1, 2, 4)
sage: sr.varstrs('x', 2)
('x200', 'x201', 'x202', 'x203', 'x210', 'x211', 'x212', 'x213')
sage: sr = mq.SR(gf2=True)
sage: sr
SR(1,1,1,4)
176 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
antiphi(l)
The operation 𝜑−1 from [MR2002] or the inverse of self.phi.
INPUT:
• l - a vector in the sense of self.is_vector
EXAMPLES:
sage: sr = mq.SR(gf2=True)
sage: A = sr.random_state_array()
sage: A
[a^2]
sage: sr.antiphi(sr.phi(A)) == A
True
field_polynomials(name, i, l=None)
Return list of field polynomials for a given round i and name name.
INPUT:
• name - variable name
• i - round number
• l - length of variable list (default: None = r*c)
EXAMPLES:
sage: sr = mq.SR(3, 1, 1, 8, gf2=True, polybori=False)
sage: sr.field_polynomials('x', 2)
[x200^2 + x200, x201^2 + x201,
x202^2 + x202, x203^2 + x203,
x204^2 + x204, x205^2 + x205,
x206^2 + x206, x207^2 + x207]
177
Sage 9.1 Reference Manual: Cryptography, Release 9.1
˓→x106*w103 + x107*w102]
39
39
True
sage: set(l3) == set(sr._inversion_polynomials_single_sbox(biaffine_
˓→only=False, correct_only=True))
True
178 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
19
True
sage: set(l3) == set(sr._inversion_polynomials_single_sbox(biaffine_
˓→only=False, correct_only=True))
True
is_vector(d)
Return True if the given matrix satisfies the conditions for a vector as it appears in the algebraic expression
of self.
INPUT:
• d - matrix
EXAMPLES:
sage: sr = mq.SR(gf2=True)
sage: sr
SR(1,1,1,4)
sage: k = sr.base_ring()
sage: A = Matrix(k, 1, 1, [k.gen()])
sage: B = sr.vector(A)
sage: sr.is_vector(A)
False
sage: sr.is_vector(B)
True
lin_matrix(length=None)
Return the Lin matrix.
If no length is provided, the standard state space size is used. The key schedule calls this method with
an explicit length argument because only self.r S-Box applications are performed in the key schedule.
INPUT:
• length - length of state space (default: None)
EXAMPLES:
mix_columns_matrix()
Return the MixColumns matrix.
179
Sage 9.1 Reference Manual: Cryptography, Release 9.1
EXAMPLES:
sage: sr = mq.SR(1, 2, 2, 4, gf2=True)
sage: s = sr.random_state_array()
sage: r1 = sr.mix_columns(s)
sage: r2 = sr.state_array(sr.mix_columns_matrix() * sr.vector(s))
sage: r1 == r2
True
phi(l, diffusion_matrix=False)
The operation 𝜑 from [MR2002]
Given a list/matrix of elements in F2𝑒 , return a matching list/matrix of elements in F2 .
INPUT:
• l - element to perform 𝜑 on.
• diffusion_matrix - if True, the given matrix l is transformed to a matrix which performs the
same operation over F2 as l over F2𝑛 (default: False).
EXAMPLES:
sage: sr = mq.SR(2, 1, 2, 4, gf2=True)
sage: k = sr.base_ring()
sage: A = matrix(k, 1, 2, [k.gen(), 0] )
sage: sr.phi(A)
[0 0]
[0 0]
[1 0]
[0 0]
shift_rows_matrix()
Return the ShiftRows matrix.
EXAMPLES:
sage: sr = mq.SR(1, 2, 2, 4, gf2=True)
sage: s = sr.random_state_array()
sage: r1 = sr.shift_rows(s)
sage: r2 = sr.state_array( sr.shift_rows_matrix() * sr.vector(s) )
sage: r1 == r2
True
vector(d=None)
Constructs a vector suitable for the algebraic representation of SR.
INPUT:
• d - values for vector (default: None)
EXAMPLES:
sage: sr = mq.SR(gf2=True)
sage: sr
SR(1,1,1,4)
sage: k = sr.base_ring()
sage: A = Matrix(k, 1, 1, [k.gen()])
sage: sr.vector(A)
[0]
[0]
(continues on next page)
180 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
181
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage: sr = mq.SR()
sage: A = sr.random_state_array()
sage: A
[a^2]
sage: sr.antiphi(sr.phi(A)) == A
True
field_polynomials(name, i, l=None)
Return list of conjugacy polynomials for a given round i and name name.
INPUT:
• name - variable name
• i - round number
• l - r*c (default: None)
EXAMPLES:
sage: sr = mq.SR(3, 1, 1, 8)
sage: sr.field_polynomials('x', 2)
[x200^2 + x201,
x201^2 + x202,
x202^2 + x203,
x203^2 + x204,
x204^2 + x205,
x205^2 + x206,
x206^2 + x207,
x207^2 + x200]
182 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
EXAMPLES:
sage: sr = mq.SR(1, 1, 1, 8)
sage: R = sr.ring()
sage: xi = Matrix(R, 8, 1, sr.vars('x', 1))
sage: wi = Matrix(R, 8, 1, sr.vars('w', 1))
sage: sr.inversion_polynomials(xi, wi, 8)
[x100*w100 + 1,
x101*w101 + 1,
x102*w102 + 1,
x103*w103 + 1,
x104*w104 + 1,
x105*w105 + 1,
x106*w106 + 1,
x107*w107 + 1]
is_vector(d)
Return True if d can be used as a vector for self.
EXAMPLES:
sage: sr = mq.SR()
sage: sr
SR(1,1,1,4)
sage: k = sr.base_ring()
sage: A = Matrix(k, 1, 1, [k.gen()])
sage: B = sr.vector(A)
sage: sr.is_vector(A)
False
sage: sr.is_vector(B)
True
lin_matrix(length=None)
Return the Lin matrix.
If no length is provided, the standard state space size is used. The key schedule calls this method with
an explicit length argument because only self.r S-Box applications are performed in the key schedule.
INPUT:
• length - length of state space (default: None)
EXAMPLES:
sage: sr = mq.SR(1, 1, 1, 4)
sage: sr.lin_matrix()
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
[ a a 1 a^3 + a^2 + a + 1]
[ a^3 + a a^2 a^2 1]
[ 1 a^3 a + 1 a + 1]
mix_columns_matrix()
Return the MixColumns matrix.
EXAMPLES:
sage: sr = mq.SR(1, 2, 2, 4)
sage: s = sr.random_state_array()
sage: r1 = sr.mix_columns(s)
sage: r2 = sr.state_array(sr.mix_columns_matrix() * sr.vector(s))
(continues on next page)
183
Sage 9.1 Reference Manual: Cryptography, Release 9.1
phi(l)
The operation 𝜑 from [MR2002]
Projects state arrays to their algebraic representation.
INPUT:
• l - element to perform 𝜑 on.
EXAMPLES:
sage: sr = mq.SR(2, 1, 2, 4)
sage: k = sr.base_ring()
sage: A = matrix(k, 1, 2, [k.gen(), 0] )
sage: sr.phi(A)
[ a 0]
[ a^2 0]
[ a + 1 0]
[a^2 + 1 0]
shift_rows_matrix()
Return the ShiftRows matrix.
EXAMPLES:
sage: sr = mq.SR(1, 2, 2, 4)
sage: s = sr.random_state_array()
sage: r1 = sr.shift_rows(s)
sage: r2 = sr.state_array( sr.shift_rows_matrix() * sr.vector(s) )
sage: r1 == r2
True
vector(d=None)
Constructs a vector suitable for the algebraic representation of SR, i.e. BES.
INPUT:
• d - values for vector, must be understood by self.phi (default:None)
EXAMPLES:
sage: sr = mq.SR()
sage: sr
SR(1,1,1,4)
sage: k = sr.base_ring()
sage: A = Matrix(k, 1, 1, [k.gen()])
sage: sr.vector(A)
[ a]
[ a^2]
[ a + 1]
[a^2 + 1]
sage.crypto.mq.sr.test_consistency(max_n=2, **kwargs)
Test all combinations of r, c, e and n in (1, 2) for consistency of random encryptions and their polynomial
systems. F2 and F2𝑒 systems are tested. This test takes a while.
INPUT:
184 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
Sage 9.1 Reference Manual: Cryptography, Release 9.1
185
Sage 9.1 Reference Manual: Cryptography, Release 9.1
186 Chapter 18. Small Scale Variants of the AES (SR) Polynomial System Generator
CHAPTER
NINETEEN
RIJNDAEL-GF
Rijndael-GF is an algebraic implementation of the AES cipher which seeks to provide a fully generalized algebraic
representation of both the whole AES cipher as well as its individual components.
This class is an algebraic implementation of the Rijndael-GF extension of the AES cipher, as described in [DR2002].
The AES cipher itself is defined to operate on a state in (F2 )8𝑛𝑡 where 𝑛𝑡 ∈ {16, 20, 24, 28, 32}. Rijndael-GF is
a generalization of AES which allows for operations in (F28 )𝑛𝑡 , enabling more algebraically sophisticated study of
AES and its variants. This implementation of Rijndael-GF is suitable for learning purposes, for comparison to other
algebraic ciphers, and for studying various techniques of algebraic cryptanalysis of AES. This cipher is different from
Mini-AES, which is a teaching tool for beginners to understand the basic structure of AES.
An algebraic implementation of Rijndael-GF is achieved by recognizing that for each round component function 𝜑
of AES (SubBytes, ShiftRows, etc.) operating on state matrices, every entry of the output matrix 𝐵 = 𝜑(𝐴) is
representable as a polynomial with variables being the entries of the input state matrix 𝐴. Correspondingly, this
implementation of Rijndael-GF provides a RijndaelGF.Round_Component_Poly_Constr class which al-
lows for creation of these such polynomials. For each round component function 𝜑 of Rijndael-GF there exists a
Round_Component_Poly_Constr object with a __call__ method of the form __call__(i, j) which
returns a polynomial representing 𝜑(𝐴)𝑖,𝑗 in terms of the entries of 𝐴. There additionally are various methods pro-
vided which allow for easy polynomial evaluation and for simple creation of Round_Component_Poly_Constr
objects representing more complex aspects of the cipher.
This approach to implementing Rijndael-GF bears some similarity to the multivariate quadratic (MQ) systems utilized
in SR, in that the MQ systems also seek to describe the AES cipher as a system of algebraic equations. Despite
this initial similarity though, Rijndael-GF and SR are quite different as this implementation seeks to provide a fully
generalized algebraic representation of both the whole AES cipher as well as its individual components, while SR is
instead a family of parameterizable variants of the AES suitable as a framework for comparing different cryptanalytic
techniques that can be brought to bear on the AES.
AUTHORS:
• Thomas Gagne (2015-06): initial version
EXAMPLES
We build Rijndael-GF with a block length of 4 and a key length of 6:
sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
sage: rgf = RijndaelGF(4, 6)
We can encrypt plaintexts and decrypt and ciphertexts by calling the encrypt and decrypt methods or by calling
the Rijndael-GF object explicitly. Note that the default input format is a hex string.
sage: plaintext = '00112233445566778899aabbccddeeff'
sage: key = '000102030405060708090a0b0c0d0e0f1011121314151617'
sage: rgf.encrypt(plaintext, key)
'dda97ca4864cdfe06eaf70a0ec0d7191'
(continues on next page)
187
Sage 9.1 Reference Manual: Cryptography, Release 9.1
˓→'11010011000010011010110001000011101110110100110100110010011011111100011011100111110011100111010011
˓→'
sage: rgf(ciphertext, key, algorithm='decrypt', format='binary') == plain
True
To build polynomials representing entries of the output matrix 𝐵 = 𝜑(𝐴) for any round compo-
nent function 𝜑, each of the round component functions (SubBytes, ShiftRows, and MixColumns) have a
Round_Component_Poly_Constr object associated with it for building polynomials. These objects can be
accessed by calling their getter functions: rgf.sub_bytes_poly(), rgf.shift_rows_poly(), and rgf.
mix_columns_poly(). Each returned object has a __call__ method which takes an index i,j and an
algorithm flag (‘encrypt’ or ‘decrypt’) and returns a polynomial representing 𝜑(𝐴)𝑖,𝑗 in terms of the entries
of 𝐴, where 𝐴 is an arbitrary state matrix and 𝜑 is the round component function associated with that particular
Round_Component_Poly_Constr object. Some of these objects’ __call__ methods also have additional
keywords to modify their behavior, and so we describe the usage of each object below.
rgf.shift_rows_poly() and rgf.mix_columns_poly() do not have any additional keywords for their
__call__ methods and we can call them as such:
sage: sr_pc = rgf.shift_rows_poly_constr()
sage: sr_pc(1, 2)
a13
sage: sr_pc(2, 3, algorithm='decrypt')
a21
rgf.sub_bytes_poly() has a single keyword no_inversion=False, which when set to True returns only
the affine transformation step of SubBytes. Below describes the usage of rgf.sub_bytes_poly()
Because of the order of the affine transformation and the inversion step in SubBytes, calling rgf.
sub_bytes_poly()(i, j, algorithm='decrypt') results in a polynomial with thousands of terms
which takes a very long time to compute. Hence, when using the decryption version of rgf.sub_bytes_poly()
with the intention of evaluating the polynomials it constructs, it is recommended to first call rgf.
sub_bytes_poly()(i, j, algorithm='decrypt', no_inversion=True) to get a polynomial rep-
resenting only the inverse affine transformation, evaluate this polynomial for a particular input block, then finally
perform the inversion step after the affine transformation polynomial has been evaluated.
sage: rgf.state_vrs
[a00 a01 a02 a03]
[a10 a11 a12 a13]
[a20 a21 a22 a23]
[a30 a31 a32 a33]
189
Sage 9.1 Reference Manual: Cryptography, Release 9.1
We can see how key variables are organized in the original key (the key used to build the rest of the subkeys) below.
Note that because key variables are subkey entries, if the key length is longer than the block length we will have entries
from multiple subkeys in the original key matrix.
sage: rgf.key_vrs
[k000 k001 k002 k003 k100 k101]
[k010 k011 k012 k013 k110 k111]
[k020 k021 k022 k023 k120 k121]
[k030 k031 k032 k033 k130 k131]
We can evaluate any of these constructed polynomials for a particular input state (in essence, calculate 𝜑(𝐴)𝑖,𝑗 ) as
such:
We can use the apply_poly method to build a matrix whose 𝑖, 𝑗 th entry equals the polynomial phi_poly(i,
j) evaluated for a particular input state, where phi_poly is the Round_Component_Poly_Constr ob-
ject associated with the round component function 𝜑. Essentially, apply_poly calculates 𝜑(𝐴), where 𝐴 is
our input state. Calling apply_poly is equivalent to applying the round component function associated this
Round_Component_Poly_Constr object to 𝐴.
Alternatively, we can pass a matrix of polynomials as input to apply_poly, which will then return an-
other matrix of polynomials. For example, rgf.state_vrs can be used as input to make each i,j th
entry of the output matrix equal phi_poly_constr(i, j), where phi_poly_constr is our inputted
Round_Component_Poly_Constr object. This matrix can then be passed through again and so on, demon-
strating how one could potentially build a matrix of polynomials representing the entire cipher.
For any of these Round_Component_Poly_Constr objects, we can change the keywords of its __call__
method when apply_poly invokes it by passing apply_poly a dictionary mapping keywords to their values.
We can build our own Round_Component_Poly_Constr objects which correspond to the compo-
sition of multiple round component functions with the compose method. To do this, if we pass
two Round_Component_Poly_Constr objects to compose where the first object corresponds to the
round component function 𝑓 and the second to the round component function 𝑔, compose will re-
turn a new Round_Component_Poly_Constr object corresponding to the function 𝑔 ∘ 𝑓 . This re-
turned Round_Component_Poly_Constr object will have the arguments of __call__(row, col,
algorithm='encrypt') and when passed an index i,j will return 𝑔(𝑓 (𝐴))𝑖,𝑗 in terms of the entries of 𝐴.
sage: rcpc(2, 1)
a01 + a12 + (x)*a23 + (x + 1)*a30
191
Sage 9.1 Reference Manual: Cryptography, Release 9.1
We can also pass keyword dictionaries of f_attr and g_attr to compose to make f and g use those keywords
during polynomial creation.
In addition to building polynomial representations of state matrices, we can also build polynomial representations of
elements of the expanded key with the expand_key_poly method. However, since the key schedule is defined
recursively, it is impossible to build polynomials for the key schedule in the same manner as we do for the round com-
ponent functions. Consequently, expand_round_key_poly() is not a Round_Component_Poly_Constr
object. Instead, expand_key_poly is a method which takes an index i,j and a round number round, and returns
a polynomial representing the 𝑖, 𝑗 th entry of the round th round key. This polynomial’s variables are entries of the
original key we built above.
sage: rgf.expand_key_poly(1, 2, 0)
k012
sage: rgf.expand_key_poly(1, 1, 1)
k111
sage: rgf.expand_key_poly(1, 2, 1)
(x^2 + 1)*k121^254 +
(x^3 + 1)*k121^253 +
(x^7 + x^6 + x^5 + x^4 + x^3 + 1)*k121^251 +
(x^5 + x^2 + 1)*k121^247 +
(x^7 + x^6 + x^5 + x^4 + x^2)*k121^239 +
k121^223 +
(x^7 + x^5 + x^4 + x^2 + 1)*k121^191 +
(x^7 + x^3 + x^2 + x + 1)*k121^127 +
k010 +
(x^6 + x^5 + x)
By changing state_chr we can alter the names of variables in polynomials representing elements from state
matrices.
193
Sage 9.1 Reference Manual: Cryptography, Release 9.1
We can also alter the name of variables in polynomials representing elements from round keys by changing
key_chr.
If 𝜑 is the round component function to which this object corresponds to, then __call__(i,j) =
𝜑(𝐴)𝑖,𝑗 , where 𝐴 is an arbitrary input matrix. Note that the polynomial returned by __call__(i,j)
will be in terms of the entries of 𝐴.
Invoking this objects __call__ method passes its arguments directly to polynomial_constr
and returns the result. In a sense, Round_Component_Poly_Constr acts as a wrapper for the
polynomial_constr method and helps ensure that each Round_Component_Poly_Constr ob-
ject will act similarly.
Since all keyword arguments of polynomial_constr must have a default value except for row and
col, we can always call a Round_Component_Poly_Constr object by __call__(row, col).
Because of this, methods such as apply_poly and compose will only call __call__(row, col)
when passed a Round_Component_Poly_Constr object. In order to change this object’s behavior
and force methods such as apply_poly to use non-default values for keywords we can pass dictionaries
mapping keywords to non-default values as input to apply_poly and compose.
sage: rgf.apply_poly(rgf.state_vrs,
....: rgf.add_round_key_poly_constr(),
....: poly_constr_attr={'round' : 9})
[a00 + k900 a01 + k901 a02 + k902 a03 + k903]
[a10 + k910 a11 + k911 a12 + k912 a13 + k913]
[a20 + k920 a21 + k921 a22 + k922 a23 + k923]
[a30 + k930 a31 + k931 a32 + k932 a33 + k933]
sage: fn = rgf.compose(rgf.add_round_key_poly_constr(),
....: rgf.add_round_key_poly_constr(),
....: f_attr={'round' : 3}, g_attr={'round' : 7})
sage: fn(2, 3)
a23 + k323 + k723
add_round_key(state, round_key)
Return the round-key addition of matrices state and round_key.
195
Sage 9.1 Reference Manual: Cryptography, Release 9.1
INPUT:
• state – The state matrix to have round_key added to.
• round_key – The round key to add to state.
OUTPUT:
• A state matrix which is the round key addition of state and round_key. This transformation is
simply the entrywise addition of these two matrices.
EXAMPLES:
add_round_key_poly_constr()
Return the Round_Component_Poly_Constr object corresponding to AddRoundKey.
EXAMPLES:
sage: ark_pc(0, 1)
a01 + k001
When invoking the returned object’s __call__ method, changing the value of
algorithm='encrypt' does nothing, since the AddRoundKey round component function is
its own inverse.
When invoking the returned object’s __call__ method, one can change the round subkey used in the
returned polynomial by changing the round=0 keyword.
When passing the returned object to methods such as apply_poly and compose, we can make these
methods use a non-default value for round=0 by passing in a dictionary mapping round to a different
value.
197
Sage 9.1 Reference Manual: Cryptography, Release 9.1
We can change the value of the keywords of poly_constr ‘s __call__ method when apply_poly
calls it by passing in a dictionary poly_constr_attr mapping keywords to their values.
sage: rgf.apply_poly(rgf.state_vrs,
....: rgf.add_round_key_poly_constr(),
....: poly_constr_attr={'round' : 5})
[a00 + k500 a01 + k501 a02 + k502 a03 + k503]
[a10 + k510 a11 + k511 a12 + k512 a13 + k513]
[a20 + k520 a21 + k521 a22 + k522 a23 + k523]
[a30 + k530 a31 + k531 a32 + k532 a33 + k533]
block_length()
Return the block length of this instantiation of Rijndael-GF.
EXAMPLES:
EXAMPLES
This function allows us to determine the polynomial representations of entries across multiple round func-
tions. For example, if we wanted a polynomial representing the 1,3 entry of a matrix after we first apply
ShiftRows and then MixColumns to that matrix, we do:
sage: fn = rgf.compose(rgf.shift_rows_poly_constr(),
....: rgf.mix_columns_poly_constr())
sage: fn(1, 3)
a03 + (x)*a10 + (x + 1)*a21 + a32
If we use compose to make a new Round_Component_Poly_Constr object, we can use that object
as input to apply_poly and compose:
If the second argument is a polynomial, then the value of algorithm is passed di-
rectly to the first argument 𝑓 during evaluation. However, if the second argument is a
Round_Component_Poly_Constr object, changing algorithm does nothing since the returned
object has its own algorithm='encrypt' keyword.
sage: f = rgf.compose(rgf.sub_bytes_poly_constr(),
....: rgf.mix_columns_poly_constr(), algorithm='decrypt')
sage: g = rgf.compose(rgf.sub_bytes_poly_constr(),
....: rgf.mix_columns_poly_constr())
sage: all(f(i,j) == g(i,j) for i in range(4) for j in range(4))
True
199
Sage 9.1 Reference Manual: Cryptography, Release 9.1
We can change the keyword attributes of the __call__ methods of f and g by passing dictionaries
f_attr and g_attr to compose.
sage: fn = rgf.compose(rgf.add_round_key_poly_constr(),
....: rgf.add_round_key_poly_constr(),
....: f_attr={'round' : 4}, g_attr={'round' : 7})
sage: fn(1, 2)
a12 + k412 + k712
expand_key(key)
Return the expanded key schedule from key.
INPUT:
• key – The key to build a key schedule from. Must be a matrix over F28 of dimensions 4 × 𝑁𝑘 .
OUTPUT:
• A length 𝑁 𝑟 list of 4 × 𝑁𝑏 matrices corresponding to the expanded key. The 𝑛 th entry of the list
corresponds to the matrix used in the add_round_key step of the 𝑛 th round.
EXAMPLES:
sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
sage: rgf = RijndaelGF(4, 6)
sage: key = '331D0084B176C3FB59CAA0EDA271B565BB5D9A2D1E4B2892'
sage: key_state = rgf._hex_to_GF(key)
sage: key_schedule = rgf.expand_key(key_state)
sage: rgf._GF_to_hex(key_schedule[0])
'331d0084b176c3fb59caa0eda271b565'
sage: rgf._GF_to_hex(key_schedule[6])
'5c5d51c4121f018d0f4f3e408ae9f78c'
201
Sage 9.1 Reference Manual: Cryptography, Release 9.1
It should be noted that expand_key_poly cannot be used with apply_poly or compose, since
expand_key_poly is not a Round_Component_Poly_Constr object.
sage: rgf.compose(rgf.sub_bytes_poly_constr(), rgf.expand_key_poly)
Traceback (most recent call last):
...
TypeError: keyword 'g' must be a Round_Component_Poly_Constr or a polynomial
˓→over Finite Field in x of size 2^8
key_length()
Return the key length of this instantiation of Rijndael-GF.
EXAMPLES:
sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
sage: rgf = RijndaelGF(4, 8)
sage: rgf.key_length()
8
mix_columns(state, algorithm=’encrypt’)
Return the application of MixColumns to the state matrix state.
INPUT:
• state – The state matrix to apply MixColumns to.
• algorithm – (default: “encrypt”) Whether to perform the encryption version of MixColumns, or
its decryption inverse. The encryption flag is “encrypt” and the decryption flag is “decrypt”.
OUTPUT:
• The state matrix over F28 which is the result of applying MixColumns to state.
EXAMPLES:
sage: from sage.crypto.mq.rijndael_gf import RijndaelGF
sage: rgf = RijndaelGF(4, 4)
(continues on next page)
mix_columns_poly_constr()
Return a Round_Component_Poly_Constr object corresponding to MixColumns.
EXAMPLES:
sage: mc_pc(1, 2)
a02 + (x)*a12 + (x + 1)*a22 + a32
sage: mc_pc(1, 0, algorithm='decrypt')
(x^3 + 1)*a00 + (x^3 + x^2 + x)*a10 + (x^3 + x + 1)*a20 + (x^3 + x^2 + 1)*a30
shift_rows(state, algorithm=’encrypt’)
Return the application of ShiftRows to the state matrix state.
INPUT:
• state – A state matrix over F28 to which ShiftRows is applied to.
• algorithm – (default: “encrypt”) Whether to perform the encryption version of ShiftRows or its
decryption inverse. The encryption flag is “encrypt” and the decryption flag is “decrypt”.
OUTPUT:
• A state matrix over F28 which is the application of ShiftRows to state.
EXAMPLES:
203
Sage 9.1 Reference Manual: Cryptography, Release 9.1
shift_rows_poly_constr()
Return a Round_Component_Poly_Constr object corresponding to ShiftRows.
EXAMPLES:
sub_bytes_poly_constr()
Return the Round_Component_Poly_Constr object corresponding to SubBytes.
EXAMPLES:
sage: sb_pc(2, 3)
(x^2 + 1)*a23^254 +
(continues on next page)
The returned object’s __call__ method has an additional keyword of no_inversion=False, which
causes the returned polynomial to represent only the affine transformation step of SubBytes.
We can build a polynomial representing the inverse transformation by setting the keyword
algorithm='decrypt'. However, the order of the affine transformation and the inversion step in
SubBytes means that this polynomial has thousands of terms and is very slow to compute. Hence, if
one wishes to build the decryption polynomial with the intention of evaluating that polynomial for a
particular input, it is strongly recommended to first call sb_pc(i, j, algorithm='decrypt',
no_inversion=True) to build a polynomial representing only the inverse affine transformation, eval-
uate this polynomial for your intended input, then finally calculate the inverse of the result.
When passing the returned object to apply_poly and compose, we can make those methods
change the keyword no_inversion of this object’s __call__ method by passing the dictionary
{'no_inversion' : True} to them.
Note that if we set algorithm='decrypt' for apply_poly, it will perform the necessary perfor-
mance enhancement described above automatically. The structure of compose, however, unfortunately
does not allow this enhancement to be employed.
205
Sage 9.1 Reference Manual: Cryptography, Release 9.1
TWENTY
OUTPUT: B a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors
for the lattice in question.
207
Sage 9.1 Reference Manual: Cryptography, Release 9.1
EXAMPLES:
Modular basis:
Random basis:
Dual modular bases are related to Regev’s famous public-key encryption [Reg2005]:
209
Sage 9.1 Reference Manual: Cryptography, Release 9.1
TWENTYONE
The Learning with Errors problem (LWE) is solving linear systems of equations where the right hand side has been
disturbed ‘slightly’ where ‘slightly’ is made precise by a noise distribution - typically a discrete Gaussian distribution.
See [Reg09] for details.
The Ring Learning with Errors problem (LWE) is solving a set of univariate polynomial equations - typically in a
cyclotomic field - where the right hand side was disturbed ‘slightly’. See [LPR2010] for details.
This module implements generators of LWE samples where parameters are chosen following proposals in the crypto-
graphic literature.
EXAMPLES:
We get 30 samples from an LWE oracle parameterised by security parameter n=20 and where the modulus and the
standard deviation of the noise are chosen as in [Reg09]:
...
((155, 22, 357, 312, 87, 298, 182, 163, 296, 181, 219, 135, 164, 308, 248, 320, 64,
˓→166, 214, 104), 152)]
We may also pass classes to the samples function, which is useful for users implementing their own oracles:
...
((1787, 2033, 1677, 331, 1562, 49, 796, 1002, 627, 98, 91, 711, 1712, 418, 2024, 163,
˓→1773, 184, 1548, 3), 1815)]
...
((1050, 1017, 1314, 1310, 1941, 2041, 484, 104, 1199, 1744, 161, 1905, 679, 1663, 531,
˓→ 1630, 168, 1559, 1040, 1719), 1006)]
211
Sage 9.1 Reference Manual: Cryptography, Release 9.1
One technical issue when working with these generators is that by default they return vectors and scalars over/in rings
modulo some 𝑞. These are represented as elements in (0, 𝑞 − 1) by Sage. However, it usually is more natural to
think of these entries as integers in (−𝑞//2, 𝑞//2). To allow for this, this module provides the option to balance the
representation. In this case vectors and scalars over/in the integers are returned:
...
((-48, 185, 118, 69, 57, 109, 109, 138, -42, -45, -16, 180, 34, 178, 20, -119, -58, -
˓→136, -46, 169), -72)]
AUTHORS:
• Martin Albrecht
• Robert Fitzpatrick
• Daniel Cabracas
• Florian Göpfert
• Michael Schneider
REFERENCES:
• [Reg09]
• [LP2011]
• [LPR2010]
• [CGW2013]
class sage.crypto.lwe.LWE(n, q, D, secret_dist=’uniform’, m=None)
Bases: sage.structure.sage_object.SageObject
Learning with Errors (LWE) oracle.
__init__(n, q, D, secret_dist=’uniform’, m=None)
Construct an LWE oracle in dimension n over a ring of order q with noise distribution D.
INPUT:
• n - dimension (integer > 0)
• q - modulus typically > n (integer > 0)
• D - an error distribution such as an instance of DiscreteGaussianDistributionIntegerSampler
or UniformSampler
• secret_dist - distribution of the secret (default: ‘uniform’); one of
sage: D = DiscreteGaussianDistributionIntegerSampler(3.0)
To test the oracle, we use the internal secret to evaluate the samples in the secret:
However, while Sage represents finite field elements between 0 and q-1 we rely on a balanced representa-
tion of those elements here. Hence, we fix the representation and recover the correct standard deviation of
the noise:
__call__()
EXAMPLES:
213
Sage 9.1 Reference Manual: Cryptography, Release 9.1
__call__()
EXAMPLES:
sage: N = 16
sage: n = euler_phi(N)
sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, 5)
sage: ringlwe = RingLWE(N, 257, D, secret_dist='uniform')
sage: ringlwe()
((226, 198, 38, 222, 222, 127, 194, 124), (11, 191, 177, 59, 105, 203, 108,
˓→42))
class sage.crypto.lwe.RingLWEConverter(ringlwe)
Bases: sage.structure.sage_object.SageObject
Wrapper callable to convert Ring-LWE oracles into LWE oracles by disregarding the additional structure.
__init__(ringlwe)
INPUT:
• ringlwe - an instance of a RingLWE
EXAMPLES:
__call__()
EXAMPLES:
215
Sage 9.1 Reference Manual: Cryptography, Release 9.1
__call__()
Return a new sample.
EXAMPLES:
__init__(lower_bound, upper_bound)
Construct a uniform sampler with bounds lower_bound and upper_bound (both endpoints inclu-
sive).
INPUT:
• lower_bound - integer
• upper_bound - integer
EXAMPLES:
__call__()
Return a new sample.
EXAMPLES:
217
Sage 9.1 Reference Manual: Cryptography, Release 9.1
sage.crypto.lwe.balance_sample(s, q=None)
Given (a,c) = s return a tuple (a',c') where a' is an integer vector with entries between -q//2 and q//2
and c is also within these bounds.
If q is given (a,c) = s may live in the integers. If q is not given, then (a,c) are assumed to live in Z/𝑞Z.
INPUT:
• s - sample of the form (a,c) where a is a vector and c is a scalar
• q - modulus (default: None)
EXAMPLES:
sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], 8, 5)
sage: rlwe = RingLWE(20, 257, D)
sage: [balance_sample(s) for s in samples(10, 8, rlwe)]
[((-64, 107, -91, -24, 120, 54, 38, -35), (-84, 121, 28, -99, 91, 54, -60, 11)),
...
((-40, -117, 35, -69, -11, 10, 122, 48), (-80, -2, 119, -91, 27, 66, 121, -1))]
Note: This function is useful to convert between Sage’s standard representation of elements in Z/𝑞Z as integers
between 0 and q-1 and the usual representation of such elements in lattice cryptography as integers between -q//2
and q//2.
((365, 227, 333, 165, 76, 328, 288, 206, 286, 42, 175, 155, 190, 275, 114, 280,
˓→45, 218, 304, 386), 143)]
((-36, -174, -68, 165, 76, -73, -113, -195, -115, 42, 175, 155, 190, -126, 114, -
˓→121, 45, -183, -97, -15), 143)]
((463, 250, 1226, 1906, 330, 933, 1014, 1061, 1322, 2035, 1849, 285, 1993, 1975,
˓→864, 1341, 41, 1955, 1818, 1357), 312)]
219
Sage 9.1 Reference Manual: Cryptography, Release 9.1
TWENTYTWO
• Index
• Module Index
• Search Page
221
Sage 9.1 Reference Manual: Cryptography, Release 9.1
c
sage.crypto.block_cipher.des, 73
sage.crypto.block_cipher.miniaes, 53
sage.crypto.block_cipher.present, 81
sage.crypto.block_cipher.sdes, 43
sage.crypto.boolean_function, 119
sage.crypto.cipher, 5
sage.crypto.classical, 7
sage.crypto.classical_cipher, 41
sage.crypto.cryptosystem, 1
sage.crypto.lattice, 207
sage.crypto.lfsr, 107
sage.crypto.lwe, 211
sage.crypto.mq.mpolynomialsystemgenerator, 153
sage.crypto.mq.rijndael_gf, 187
sage.crypto.mq.sr, 157
sage.crypto.public_key.blum_goldwasser, 91
sage.crypto.sbox, 129
sage.crypto.sboxes, 145
sage.crypto.stream, 99
sage.crypto.stream_cipher, 103
sage.crypto.util, 111
223
Sage 9.1 Reference Manual: Cryptography, Release 9.1
Symbols
__call__() (sage.crypto.block_cipher.des.DES method), 75
__call__() (sage.crypto.block_cipher.des.DES_KS method), 78
__call__() (sage.crypto.block_cipher.present.PRESENT method), 83
__call__() (sage.crypto.block_cipher.present.PRESENT_KS method), 88
__call__() (sage.crypto.lwe.LWE method), 213
__call__() (sage.crypto.lwe.RingLWE method), 215
__call__() (sage.crypto.lwe.RingLWEConverter method), 215
__call__() (sage.crypto.lwe.UniformPolynomialSampler method), 217
__call__() (sage.crypto.lwe.UniformSampler method), 217
__init__() (sage.crypto.block_cipher.des.DES method), 75
__init__() (sage.crypto.block_cipher.des.DES_KS method), 78
__init__() (sage.crypto.block_cipher.present.PRESENT method), 82
__init__() (sage.crypto.block_cipher.present.PRESENT_KS method), 88
__init__() (sage.crypto.lwe.LWE method), 212
__init__() (sage.crypto.lwe.LindnerPeikert method), 213
__init__() (sage.crypto.lwe.Regev method), 214
__init__() (sage.crypto.lwe.RingLWE method), 214
__init__() (sage.crypto.lwe.RingLWEConverter method), 215
__init__() (sage.crypto.lwe.RingLindnerPeikert method), 216
__init__() (sage.crypto.lwe.UniformNoiseLWE method), 216
__init__() (sage.crypto.lwe.UniformPolynomialSampler method), 216
__init__() (sage.crypto.lwe.UniformSampler method), 217
A
absolut_indicator() (sage.crypto.boolean_function.BooleanFunction method), 120
absolute_autocorrelation() (sage.crypto.boolean_function.BooleanFunction method), 120
absolute_indicator() (sage.crypto.boolean_function.BooleanFunction method), 121
absolute_walsh_spectrum() (sage.crypto.boolean_function.BooleanFunction method), 121
add_key() (sage.crypto.block_cipher.miniaes.MiniAES method), 57
add_round_key() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 195
add_round_key() (sage.crypto.mq.sr.SR_generic method), 164
add_round_key_poly_constr() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 196
AffineCipher (class in sage.crypto.classical_cipher), 41
AffineCryptosystem (class in sage.crypto.classical), 7
algebraic_degree() (sage.crypto.boolean_function.BooleanFunction method), 121
algebraic_immunity() (sage.crypto.boolean_function.BooleanFunction method), 121
225
Sage 9.1 Reference Manual: Cryptography, Release 9.1
B
balance_sample() (in module sage.crypto.lwe), 218
base_ring() (sage.crypto.mq.sr.SR_generic method), 164
bin_to_ascii() (in module sage.crypto.util), 112
binary_to_GF() (sage.crypto.block_cipher.miniaes.MiniAES method), 58
binary_to_integer() (sage.crypto.block_cipher.miniaes.MiniAES method), 59
block_length() (sage.crypto.block_cipher.miniaes.MiniAES method), 60
block_length() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 43
block_length() (sage.crypto.classical.HillCryptosystem method), 18
block_length() (sage.crypto.cryptosystem.Cryptosystem method), 2
block_length() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 198
block_order() (sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator method), 153
block_order() (sage.crypto.mq.sr.SR_generic method), 164
blum_blum_shub() (in module sage.crypto.stream), 99
BlumGoldwasser (class in sage.crypto.public_key.blum_goldwasser), 91
BooleanFunction (class in sage.crypto.boolean_function), 119
BooleanFunctionIterator (class in sage.crypto.boolean_function), 127
boomerang_connectivity_table() (sage.crypto.sbox.SBox method), 130
boomerang_uniformity() (sage.crypto.sbox.SBox method), 131
bracken_leander() (in module sage.crypto.sboxes), 149
brute_force() (sage.crypto.classical.AffineCryptosystem method), 9
brute_force() (sage.crypto.classical.ShiftCryptosystem method), 22
C
carlet_tang_tang_liao() (in module sage.crypto.sboxes), 149
carmichael_lambda() (in module sage.crypto.util), 113
Cipher (class in sage.crypto.cipher), 5
cipher_codomain() (sage.crypto.cryptosystem.Cryptosystem method), 2
cipher_domain() (sage.crypto.cryptosystem.Cryptosystem method), 2
ciphertext_space() (sage.crypto.cryptosystem.Cryptosystem method), 3
cnf() (sage.crypto.sbox.SBox method), 131
codomain() (sage.crypto.cipher.Cipher method), 5
component_function() (sage.crypto.sbox.SBox method), 133
compose() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 198
connection_polynomial() (sage.crypto.stream_cipher.LFSRCipher method), 103
convert_to_vector() (in module sage.crypto.block_cipher.des), 79
convert_to_vector() (in module sage.crypto.block_cipher.present), 89
correlation_immunity() (sage.crypto.boolean_function.BooleanFunction method), 123
226 Index
Sage 9.1 Reference Manual: Cryptography, Release 9.1
D
decimating_cipher() (sage.crypto.stream_cipher.ShrinkingGeneratorCipher method), 104
deciphering() (sage.crypto.classical.AffineCryptosystem method), 11
deciphering() (sage.crypto.classical.HillCryptosystem method), 18
deciphering() (sage.crypto.classical.ShiftCryptosystem method), 25
deciphering() (sage.crypto.classical.SubstitutionCryptosystem method), 33
deciphering() (sage.crypto.classical.TranspositionCryptosystem method), 36
deciphering() (sage.crypto.classical.VigenereCryptosystem method), 38
decrypt() (sage.crypto.block_cipher.des.DES method), 76
decrypt() (sage.crypto.block_cipher.miniaes.MiniAES method), 60
decrypt() (sage.crypto.block_cipher.present.PRESENT method), 84
decrypt() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 44
decrypt() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 200
decrypt() (sage.crypto.public_key.blum_goldwasser.BlumGoldwasser method), 92
derivative() (sage.crypto.boolean_function.BooleanFunction method), 123
derivative() (sage.crypto.sbox.SBox method), 133
DES (class in sage.crypto.block_cipher.des), 74
DES_KS (class in sage.crypto.block_cipher.des), 77
difference_distribution_table() (sage.crypto.sbox.SBox method), 134
differential_branch_number() (sage.crypto.sbox.SBox method), 134
domain() (sage.crypto.cipher.Cipher method), 5
E
enciphering() (sage.crypto.classical.AffineCryptosystem method), 12
enciphering() (sage.crypto.classical.HillCryptosystem method), 18
enciphering() (sage.crypto.classical.ShiftCryptosystem method), 26
enciphering() (sage.crypto.classical.SubstitutionCryptosystem method), 34
enciphering() (sage.crypto.classical.TranspositionCryptosystem method), 36
enciphering() (sage.crypto.classical.VigenereCryptosystem method), 38
encoding() (sage.crypto.classical.AffineCryptosystem method), 12
encoding() (sage.crypto.classical.HillCryptosystem method), 19
encoding() (sage.crypto.classical.ShiftCryptosystem method), 26
encoding() (sage.crypto.classical.SubstitutionCryptosystem method), 34
encoding() (sage.crypto.classical.TranspositionCryptosystem method), 36
encoding() (sage.crypto.classical.VigenereCryptosystem method), 38
encoding() (sage.crypto.stream.LFSRCryptosystem method), 99
encoding() (sage.crypto.stream.ShrinkingGeneratorCryptosystem method), 99
encrypt() (sage.crypto.block_cipher.des.DES method), 76
encrypt() (sage.crypto.block_cipher.miniaes.MiniAES method), 62
encrypt() (sage.crypto.block_cipher.present.PRESENT method), 84
encrypt() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 44
encrypt() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 200
encrypt() (sage.crypto.public_key.blum_goldwasser.BlumGoldwasser method), 94
expand_key() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 201
expand_key_poly() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 201
F
feistel_construction() (in module sage.crypto.sbox), 143
Index 227
Sage 9.1 Reference Manual: Cryptography, Release 9.1
G
gen_lattice() (in module sage.crypto.lattice), 207
GF_to_binary() (sage.crypto.block_cipher.miniaes.MiniAES method), 54
GF_to_integer() (sage.crypto.block_cipher.miniaes.MiniAES method), 56
gold() (in module sage.crypto.sboxes), 149
H
has_blum_prime() (in module sage.crypto.util), 114
has_linear_structure() (sage.crypto.boolean_function.BooleanFunction method), 123
has_linear_structure() (sage.crypto.sbox.SBox method), 135
hex_str() (sage.crypto.mq.sr.SR_generic method), 165
hex_str_matrix() (sage.crypto.mq.sr.SR_generic method), 165
hex_str_vector() (sage.crypto.mq.sr.SR_generic method), 165
HillCipher (class in sage.crypto.classical_cipher), 41
HillCryptosystem (class in sage.crypto.classical), 17
I
initial_permutation() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 45
initial_state() (sage.crypto.stream_cipher.LFSRCipher method), 104
input_size() (sage.crypto.sbox.SBox method), 135
integer_to_binary() (sage.crypto.block_cipher.miniaes.MiniAES method), 64
integer_to_GF() (sage.crypto.block_cipher.miniaes.MiniAES method), 63
interpolation_polynomial() (sage.crypto.sbox.SBox method), 135
inverse() (sage.crypto.classical_cipher.HillCipher method), 41
inverse() (sage.crypto.classical_cipher.SubstitutionCipher method), 41
inverse() (sage.crypto.classical_cipher.TranspositionCipher method), 41
inverse() (sage.crypto.classical_cipher.VigenereCipher method), 41
inverse() (sage.crypto.sbox.SBox method), 136
inverse_key() (sage.crypto.classical.AffineCryptosystem method), 13
inverse_key() (sage.crypto.classical.HillCryptosystem method), 19
inverse_key() (sage.crypto.classical.ShiftCryptosystem method), 27
inverse_key() (sage.crypto.classical.SubstitutionCryptosystem method), 34
inverse_key() (sage.crypto.classical.TranspositionCryptosystem method), 37
inverse_key() (sage.crypto.classical.VigenereCryptosystem method), 39
inversion_polynomials() (sage.crypto.mq.sr.SR_gf2 method), 177
inversion_polynomials() (sage.crypto.mq.sr.SR_gf2n method), 182
inversion_polynomials_single_sbox() (sage.crypto.mq.sr.SR_gf2 method), 178
inversion_polynomials_single_sbox() (sage.crypto.mq.sr.SR_gf2_2 method), 181
is_almost_bent() (sage.crypto.sbox.SBox method), 136
is_apn() (sage.crypto.sbox.SBox method), 136
is_balanced() (sage.crypto.boolean_function.BooleanFunction method), 124
is_balanced() (sage.crypto.sbox.SBox method), 136
is_bent() (sage.crypto.boolean_function.BooleanFunction method), 124
is_bent() (sage.crypto.sbox.SBox method), 136
is_blum_prime() (in module sage.crypto.util), 115
228 Index
Sage 9.1 Reference Manual: Cryptography, Release 9.1
K
kasami() (in module sage.crypto.sboxes), 150
key() (sage.crypto.cipher.Cipher method), 5
key_length() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 202
key_schedule() (sage.crypto.mq.sr.SR_generic method), 166
key_schedule_polynomials() (sage.crypto.mq.sr.SR_generic method), 166
key_space() (sage.crypto.cryptosystem.Cryptosystem method), 3
keystream_cipher() (sage.crypto.stream_cipher.ShrinkingGeneratorCipher method), 104
L
least_significant_bits() (in module sage.crypto.util), 115
left_shift() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 46
lfsr_autocorrelation() (in module sage.crypto.lfsr), 108
lfsr_connection_polynomial() (in module sage.crypto.lfsr), 108
lfsr_sequence() (in module sage.crypto.lfsr), 109
LFSRCipher (class in sage.crypto.stream_cipher), 103
LFSRCryptosystem (class in sage.crypto.stream), 99
lin_matrix() (sage.crypto.mq.sr.SR_gf2 method), 179
lin_matrix() (sage.crypto.mq.sr.SR_gf2n method), 183
LindnerPeikert (class in sage.crypto.lwe), 213
linear_approximation_table() (sage.crypto.sbox.SBox method), 138
linear_branch_number() (sage.crypto.sbox.SBox method), 139
linear_layer() (sage.crypto.block_cipher.present.PRESENT method), 86
linear_structures() (sage.crypto.boolean_function.BooleanFunction method), 125
linear_structures() (sage.crypto.sbox.SBox method), 140
linearity() (sage.crypto.sbox.SBox method), 140
list_to_string() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 46
LWE (class in sage.crypto.lwe), 212
M
max_degree() (sage.crypto.sbox.SBox method), 140
maximal_difference_probability() (sage.crypto.sbox.SBox method), 140
maximal_difference_probability_absolute() (sage.crypto.sbox.SBox method), 141
maximal_linear_bias_absolute() (sage.crypto.sbox.SBox method), 141
maximal_linear_bias_relative() (sage.crypto.sbox.SBox method), 141
min_degree() (sage.crypto.sbox.SBox method), 141
MiniAES (class in sage.crypto.block_cipher.miniaes), 53
misty_construction() (in module sage.crypto.sbox), 144
Index 229
Sage 9.1 Reference Manual: Cryptography, Release 9.1
N
new_generator() (sage.crypto.mq.sr.SR_generic method), 167
nibble_sub() (sage.crypto.block_cipher.miniaes.MiniAES method), 66
niho() (in module sage.crypto.sboxes), 150
nonlinearity() (sage.crypto.boolean_function.BooleanFunction method), 126
nonlinearity() (sage.crypto.sbox.SBox method), 141
number_rounds() (sage.crypto.mq.rijndael_gf.RijndaelGF method), 203
nvariables() (sage.crypto.boolean_function.BooleanFunction method), 126
O
output_size() (sage.crypto.sbox.SBox method), 142
P
period() (sage.crypto.cryptosystem.Cryptosystem method), 3
permutation10() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 47
permutation4() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 47
permutation8() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 48
permute_substitute() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 49
phi() (sage.crypto.mq.sr.SR_gf2 method), 180
phi() (sage.crypto.mq.sr.SR_gf2n method), 184
plaintext_space() (sage.crypto.cryptosystem.Cryptosystem method), 3
polynomial_system() (sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator method),
153
polynomial_system() (sage.crypto.mq.sr.SR_generic method), 167
polynomials() (sage.crypto.sbox.SBox method), 142
PRESENT (class in sage.crypto.block_cipher.present), 81
PRESENT_KS (class in sage.crypto.block_cipher.present), 87
private_key() (sage.crypto.public_key.blum_goldwasser.BlumGoldwasser method), 95
public_key() (sage.crypto.public_key.blum_goldwasser.BlumGoldwasser method), 96
PublicKeyCipher (class in sage.crypto.cipher), 5
PublicKeyCryptosystem (class in sage.crypto.cryptosystem), 4
R
random_blum_prime() (in module sage.crypto.util), 116
random_boolean_function() (in module sage.crypto.boolean_function), 128
random_element() (sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator method), 153
random_element() (sage.crypto.mq.sr.SR_generic method), 169
random_key() (sage.crypto.block_cipher.miniaes.MiniAES method), 69
random_key() (sage.crypto.block_cipher.sdes.SimplifiedDES method), 50
random_key() (sage.crypto.classical.AffineCryptosystem method), 14
random_key() (sage.crypto.classical.HillCryptosystem method), 19
230 Index
Sage 9.1 Reference Manual: Cryptography, Release 9.1
S
sage.crypto.block_cipher.des (module), 73
sage.crypto.block_cipher.miniaes (module), 53
sage.crypto.block_cipher.present (module), 81
sage.crypto.block_cipher.sdes (module), 43
sage.crypto.boolean_function (module), 119
sage.crypto.cipher (module), 5
sage.crypto.classical (module), 7
sage.crypto.classical_cipher (module), 41
sage.crypto.cryptosystem (module), 1
sage.crypto.lattice (module), 207
sage.crypto.lfsr (module), 107
sage.crypto.lwe (module), 211
sage.crypto.mq.mpolynomialsystemgenerator (module), 153
sage.crypto.mq.rijndael_gf (module), 187
sage.crypto.mq.sr (module), 157
sage.crypto.public_key.blum_goldwasser (module), 91
sage.crypto.sbox (module), 129
sage.crypto.sboxes (module), 145
sage.crypto.stream (module), 99
sage.crypto.stream_cipher (module), 103
sage.crypto.util (module), 111
samples() (in module sage.crypto.lwe), 218
SBox (class in sage.crypto.sbox), 129
Index 231
Sage 9.1 Reference Manual: Cryptography, Release 9.1
T
test_consistency() (in module sage.crypto.mq.sr), 184
to_bits() (sage.crypto.sbox.SBox method), 143
TranspositionCipher (class in sage.crypto.classical_cipher), 41
TranspositionCryptosystem (class in sage.crypto.classical), 35
truth_table() (sage.crypto.boolean_function.BooleanFunction method), 126
U
UniformNoiseLWE (class in sage.crypto.lwe), 216
UniformPolynomialSampler (class in sage.crypto.lwe), 216
UniformSampler (class in sage.crypto.lwe), 217
unpickle_BooleanFunction() (in module sage.crypto.boolean_function), 128
232 Index
Sage 9.1 Reference Manual: Cryptography, Release 9.1
V
v() (in module sage.crypto.sboxes), 151
varformatstr() (sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator method), 154
varformatstr() (sage.crypto.mq.sr.SR_generic method), 174
variable_dict() (sage.crypto.mq.sr.SR_generic method), 175
vars() (sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator method), 154
vars() (sage.crypto.mq.sr.SR_generic method), 175
varstr() (sage.crypto.mq.sr.SR_generic method), 176
varstrs() (sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator method), 155
varstrs() (sage.crypto.mq.sr.SR_generic method), 176
vector() (sage.crypto.mq.sr.SR_gf2 method), 180
vector() (sage.crypto.mq.sr.SR_gf2n method), 184
VigenereCipher (class in sage.crypto.classical_cipher), 41
VigenereCryptosystem (class in sage.crypto.classical), 37
W
walsh_hadamard_transform() (sage.crypto.boolean_function.BooleanFunction method), 127
welch() (in module sage.crypto.sboxes), 151
Index 233