# Ticket #6841: trac_6841-shift-cipher.patch

File trac_6841-shift-cipher.patch, 100.0 KB (added by mvngu, 12 years ago)

based on Sage 4.1.2.alpha2

• ## doc/en/reference/monoids.rst

# HG changeset patch
# User Minh Van Nguyen <nguyenminh2@gmail.com>
# Date 1251613443 25200
# Node ID 5d9b9378ee4ffe01f1caa3a334057f55c8ae3eba
# Parent  7fc8b5794aca5e6bb3870659f305040e8f99cb00
trac 6841: the shift cryptosystem

diff -r 7fc8b5794aca -r 5d9b9378ee4f doc/en/reference/monoids.rst
 a sage/monoids/free_monoid sage/monoids/free_monoid_element sage/monoids/free_abelian_monoid sage/monoids/free_abelian_monoid_element No newline at end of file sage/monoids/free_abelian_monoid_element sage/monoids/string_monoid_element sage/monoids/string_monoid
• ## sage/crypto/all.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/all.py
 a from classical import ( HillCryptosystem, SubstitutionCryptosystem, ShiftCryptosystem, TranspositionCryptosystem, VigenereCryptosystem)
• ## sage/crypto/cipher.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/cipher.py
 a """ Ciphers. Ciphers """ #***************************************************************************** def __eq__(self, right): return type(self) == type(right) and self._parent == right._parent and self._key == right._key def __repr__(self): return str(self._key) def _repr_(self): r""" Return the string representation of this cipher. EXAMPLES:: sage: S = ShiftCryptosystem(AlphabeticStrings()) sage: S(13) Shift cipher on Free alphabetic string monoid on A-Z """ # return str(self._key) return "Cipher on %s" % self.parent().cipher_domain() def key(self): return self._key # was str(self._key)
• ## sage/crypto/classical.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/classical.py
 a """ r""" Classical Cryptosystems A convenient user interface to various classical ciphers. These include: - Hill or matrix cipher; see :class:HillCryptosystem - shift cipher; see :class:ShiftCryptosystem - substitution cipher; see :class:SubstitutionCryptosystem - transposition cipher; see :class:TranspositionCryptosystem - Vigenere cipher; see :class:VigenereCryptosystem These classical cryptosystems support alphabets such as: - the capital letters of the English alphabet; see :func:AlphabeticStrings()  - the hexadecimal number system; see :func:HexadecimalStrings()  - the binary number system; see :func:BinaryStrings()  - the octal number system; see :func:OctalStrings()  - the radix-64 number system; see :func:Radix64Strings()  AUTHORS: - David Kohel (2007): initial version with the Hill, substitution, transposition, and Vigenere cryptosystems. - Minh Van Nguyen (2009-08): shift cipher """ #***************************************************************************** #                  http://www.gnu.org/licenses/ #***************************************************************************** # TODO: check off this todo list: # - methods for cryptanalysis of the shift cipher # - implement the affine cipher # - methods for cryptanalysis of the affine cipher # - methods to cryptanalyze the Hill, substitution, transposition, and #   Vigenere ciphers from sage.monoids.string_monoid import StringMonoid_class, AlphabeticStringMonoid from sage.monoids.string_monoid_element import StringMonoidElement from sage.monoids.string_ops import strip_encoding from cryptosystem import SymmetricKeyCryptosystem from classical_cipher import ( HillCipher, ShiftCipher, SubstitutionCipher, TranspositionCipher, VigenereCipher) [2 2 3] sage: e = E(A) sage: e [1 0 1] [0 1 1] [2 2 3] Hill cipher on Free alphabetic string monoid on A-Z of block length 3 sage: e(S("LAMAISONBLANCHE")) JYVKSKQPELAYKPV [2 2 3] sage: e = E(A) sage: e [1 0 1] [0 1 1] [2 2 3] Hill cipher on Free alphabetic string monoid on A-Z of block length 3 sage: m = S("LAMAISONBLANCHE") sage: e(m) JYVKSKQPELAYKPV e = self(A) return e(M) class ShiftCryptosystem(SymmetricKeyCryptosystem): r""" Create a shift cryptosystem. Let A = \{ a_0, a_1, a_2, \dots, a_{n-1} \} be a non-empty alphabet consisting of n unique elements. Define a mapping f : A \longrightarrow \ZZ/ n\ZZ from the alphabet A to the set \ZZ / n\ZZ of integers modulo n, given by f(a_i) = i. Thus we can identify each element of the alphabet A with a unique integer 0 \leq i < n. A key of the shift cipher is an integer 0 \leq k < n. Therefore the key space is \ZZ / n\ZZ. Since we assume that A does not have repeated elements, the mapping f : A \longrightarrow \ZZ/ n\ZZ is bijective. Encryption works by moving along the alphabet by k positions, with wrap around. Decryption reverses the process by moving backwards by k positions, with wrap around. More generally, let k be a secret key, i.e. an element of the key space, and let p be a plaintext character and consequently p \in \ZZ / n\ZZ. Then the ciphertext character c corresponding to p is given by .. MATH:: c \equiv p + k \pmod{n} Similarly, given a ciphertext character c \in \ZZ / n\ZZ and a secret key k, we can recover the corresponding plaintext character as follows: .. MATH:: p \equiv c - k \pmod{n} Use the bijection f : A \longrightarrow \ZZ/ n\ZZ to convert c and p back to elements of the alphabet A. Currently, the following alphabets are supported for the shift cipher: - capital letters of the English alphabet as implemented in :func:AlphabeticStrings()  - the alphabet consisting of the hexadecimal number system as implemented in :func:HexadecimalStrings()  - the alphabet consisting of the binary number system as implemented in :func: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 The previous example can also be done as follows:: 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 Over the hexadecimal number system:: sage: S = ShiftCryptosystem(HexadecimalStrings()); S Shift cryptosystem on Free hexadecimal string monoid sage: P = S.encoding("Encryption & decryption shifts along the alphabet."); P 456e6372797074696f6e20262064656372797074696f6e2073686966747320616c6f6e672074686520616c7068616265742e sage: K = 5 sage: C = S.enciphering(K, P); C 9ab3b8c7cec5c9beb4b3757b75b9bab8c7cec5c9beb4b375c8bdbebbc9c875b6b1b4b3bc75c9bdba75b6b1c5bdb6b7bac973 sage: S.deciphering(K, C) 456e6372797074696f6e20262064656372797074696f6e2073686966747320616c6f6e672074686520616c7068616265742e sage: S.deciphering(K, C) == P True And over the binary number system:: sage: S = ShiftCryptosystem(BinaryStrings()); S Shift cryptosystem on Free binary string monoid sage: P = S.encoding("The binary alphabet is very insecure."); P 01010100011010000110010100100000011000100110100101101110011000010111001001111001001000000110000101101100011100000110100001100001011000100110010101110100001000000110100101110011001000000111011001100101011100100111100100100000011010010110111001110011011001010110001101110101011100100110010100101110 sage: K = 1 sage: C = S.enciphering(K, P); C 10101011100101111001101011011111100111011001011010010001100111101000110110000110110111111001111010010011100011111001011110011110100111011001101010001011110111111001011010001100110111111000100110011010100011011000011011011111100101101001000110001100100110101001110010001010100011011001101011010001 sage: S.deciphering(K, C) 01010100011010000110010100100000011000100110100101101110011000010111001001111001001000000110000101101100011100000110100001100001011000100110010101110100001000000110100101110011001000000111011001100101011100100111100100100000011010010110111001110011011001010110001101110101011100100110010100101110 sage: S.deciphering(K, C) == P True A shift cryptosystem with key k = 3 is commonly referred to as the Caesar cipher. Create a Caesar cipher over the upper-case letters of the English alphabet:: sage: caesar = ShiftCryptosystem(AlphabeticStrings()) sage: K = 3 sage: P = caesar.encoding("abcdef"); P ABCDEF sage: C = caesar.enciphering(K, P); C DEFGHI sage: caesar.deciphering(K, C) == P True Generate a random key for encryption and decryption:: 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 TESTS: Currently, the octal number system is not supported as an alphabet for this shift cryptosystem:: sage: ShiftCryptosystem(OctalStrings()) Traceback (most recent call last): ... TypeError: A (= Free octal string monoid) is not supported as a cipher domain of this shift cryptosystem. Nor is the radix-64 number system supported:: sage: ShiftCryptosystem(Radix64Strings()) Traceback (most recent call last): ... TypeError: A (= Free radix 64 string monoid) is not supported as a cipher domain of this shift cryptosystem. Testing of dumping and loading objects:: sage: SA = ShiftCryptosystem(AlphabeticStrings()) sage: SA == loads(dumps(SA)) True sage: SH = ShiftCryptosystem(HexadecimalStrings()) sage: SH == loads(dumps(SH)) True sage: SB = ShiftCryptosystem(BinaryStrings()) sage: SB == loads(dumps(SB)) True The key K must satisfy the inequality 0 \leq K < n with n being the size of the plaintext, ciphertext, and key spaces. For the shift cryptosystem, all these spaces are the same alphabet. This inequality must be satisfied for each of the supported alphabets. The capital letters of the English alphabet:: sage: S = ShiftCryptosystem(AlphabeticStrings()) sage: S(2 + S.alphabet_size()) Traceback (most recent call last): ... ValueError: K (=28) is outside the range of acceptable values for a key of this shift cryptosystem. sage: S(-2) Traceback (most recent call last): ... ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem. The hexadecimal number system:: sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: S(1 + S.alphabet_size()) Traceback (most recent call last): ... ValueError: K (=17) is outside the range of acceptable values for a key of this shift cryptosystem. sage: S(-1) Traceback (most recent call last): ... ValueError: K (=-1) is outside the range of acceptable values for a key of this shift cryptosystem. The binary number system:: sage: S = ShiftCryptosystem(BinaryStrings()) sage: S(1 + S.alphabet_size()) Traceback (most recent call last): ... ValueError: K (=3) is outside the range of acceptable values for a key of this shift cryptosystem. sage: S(-2) Traceback (most recent call last): ... ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem. """ def __init__(self, A): r""" See ShiftCryptosystem for full documentation. Create a shift cryptosystem defined over the alphabet A. INPUT: - A -- a string monoid over some alphabet; this is the non-empty alphabet over which the plaintext and ciphertext spaces are defined. OUTPUT: - A shift cryptosystem over the alphabet A. EXAMPLES:: 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 """ # sanity check from sage.monoids.string_monoid import ( AlphabeticStringMonoid, BinaryStringMonoid, HexadecimalStringMonoid ) if not isinstance(A, ( AlphabeticStringMonoid, BinaryStringMonoid, HexadecimalStringMonoid )): raise TypeError("A (= %s) is not supported as a cipher domain of this shift cryptosystem." % A) # Initialize the shift cryptosystem with the plaintext, ciphertext, # and key spaces. SymmetricKeyCryptosystem.__init__(self, A, A, IntegerModRing(A.ngens())) def __call__(self, K): r""" Create a shift cipher with key K. INPUT: - K -- a secret key; this key is used for both encryption and decryption. For the shift cryptosystem whose plaintext and ciphertext spaces are A, a key is any integer k such that 0 \leq k < n where n is the size or cardinality of the set A. OUTPUT: - A shift cipher with secret key K. EXAMPLES:: sage: S = ShiftCryptosystem(AlphabeticStrings()) sage: P = S.encoding("Shifting sand."); P SHIFTINGSAND sage: K = 3 sage: E = S(K); E Shift cipher on Free alphabetic string monoid on A-Z sage: E(P) VKLIWLQJVDQG sage: D = S(S.inverse_key(K)); D Shift cipher on Free alphabetic string monoid on A-Z sage: D(E(P)) SHIFTINGSAND TESTS: The key K must satisfy the inequality 0 \leq K < n with n being the size of the plaintext, ciphertext, and key spaces. For the shift cryptosystem, all these spaces are the same alphabet. This inequality must be satisfied for each of the supported alphabets. The capital letters of the English alphabet:: sage: S = ShiftCryptosystem(AlphabeticStrings()) sage: S(2 + S.alphabet_size()) Traceback (most recent call last): ... ValueError: K (=28) is outside the range of acceptable values for a key of this shift cryptosystem. sage: S(-2) Traceback (most recent call last): ... ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem. The hexadecimal number system:: sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: S(1 + S.alphabet_size()) Traceback (most recent call last): ... ValueError: K (=17) is outside the range of acceptable values for a key of this shift cryptosystem. sage: S(-1) Traceback (most recent call last): ... ValueError: K (=-1) is outside the range of acceptable values for a key of this shift cryptosystem. The binary number system:: sage: S = ShiftCryptosystem(BinaryStrings()) sage: S(1 + S.alphabet_size()) Traceback (most recent call last): ... ValueError: K (=3) is outside the range of acceptable values for a key of this shift cryptosystem. sage: S(-2) Traceback (most recent call last): ... ValueError: K (=-2) is outside the range of acceptable values for a key of this shift cryptosystem. """ # Sanity check: the key K must satisfy the inequality # 0 <= K < n with n being the size of the plaintext, ciphertext, and # key spaces. For the shift cryptosystem, all these spaces are the # same alphabet. if 0 <= K < self.alphabet_size(): return ShiftCipher(self, K) # from sage.rings.integer_mod import Mod # return ShiftCipher(self, Mod(K, self.alphabet_size()).lift()) else: raise ValueError("K (=%s) is outside the range of acceptable values for a key of this shift cryptosystem." % K) def _repr_(self): r""" Return the string representation of self. EXAMPLES:: sage: ShiftCryptosystem(AlphabeticStrings()) Shift cryptosystem on Free alphabetic string monoid on A-Z sage: ShiftCryptosystem(HexadecimalStrings()) Shift cryptosystem on Free hexadecimal string monoid sage: ShiftCryptosystem(BinaryStrings()) Shift cryptosystem on Free binary string monoid """ # The shift cipher has the plaintext and ciphertext spaces defined # over the same non-empty alphabet. The cipher domain is the same # as the alphabet used for the plaintext and ciphertext spaces. return "Shift cryptosystem on %s" % self.cipher_domain() def deciphering(self, K, C): r""" 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 k satisfying the inequality 0 \leq k < n, where n 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 :func: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 Decryption over the hexadecimal number system:: sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: P = S.encoding("Shift me now."); P 5368696674206d65206e6f772e sage: K = 7 sage: C = S.enciphering(K, P); C cadfd0ddeb97d4dc97d5d6ee95 sage: S.deciphering(K, C) == P True Decryption over the binary number system:: sage: S = ShiftCryptosystem(BinaryStrings()) sage: P = S.encoding("OK, enough shifting."); P 0100111101001011001011000010000001100101011011100110111101110101011001110110100000100000011100110110100001101001011001100111010001101001011011100110011100101110 sage: K = 1 sage: C = S.enciphering(K, P); C 1011000010110100110100111101111110011010100100011001000010001010100110001001011111011111100011001001011110010110100110011000101110010110100100011001100011010001 sage: S.deciphering(K, C) == P True """ E = self(self.inverse_key(K)) return E(C) def enciphering(self, K, P): r""" 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 k satisfying the inequality 0 \leq k < n, where n 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 :func: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 Encryption over the hexadecimal number system:: sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: P = S.encoding("Capitalize with the shift key."); P 4361706974616c697a65207769746820746865207368696674206b65792e sage: K = 5 sage: S.enciphering(K, P) 98b6c5bec9b6b1becfba75ccbec9bd75c9bdba75c8bdbebbc975b0bace73 Encryption over the binary number system:: sage: S = ShiftCryptosystem(BinaryStrings()) sage: P = S.encoding("Don't shift."); P 010001000110111101101110001001110111010000100000011100110110100001101001011001100111010000101110 sage: K = 1 sage: S.enciphering(K, P) 101110111001000010010001110110001000101111011111100011001001011110010110100110011000101111010001 """ E = self(K) return E(P) def encoding(self, S): r""" The encoding of the string S over the string monoid of this shift cipher. For example, if the string monoid of this cryptosystem is :class: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 :func:AlphabeticStrings()  - the alphabet consisting of the hexadecimal number system as implemented in :func:HexadecimalStrings()  - the alphabet consisting of the binary number system as implemented in :func:BinaryStrings()  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: S = ShiftCryptosystem(AlphabeticStrings()) sage: S.encoding("Shift cipher on capital letters of the English alphabet.") SHIFTCIPHERONCAPITALLETTERSOFTHEENGLISHALPHABET Encoding over the binary system:: sage: S = ShiftCryptosystem(BinaryStrings()) sage: S.encoding("Binary") 010000100110100101101110011000010111001001111001 Encoding over the hexadecimal system:: sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: S.encoding("Over hexadecimal system.") 4f7665722068657861646563696d616c2073797374656d2e 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("") """ D = self.cipher_domain() if isinstance(D, AlphabeticStringMonoid): return D(strip_encoding(S)) try: return D.encoding(S) except: raise TypeError("Argument S = %s does not encode in the cipher domain" % S) def inverse_key(self, K): r""" The inverse key corresponding to the key K. For the shift cipher, the inverse key corresponding to K is -K \bmod n, where n > 0 is the size of the cipher domain, i.e. the plaintext/ciphertext space. A key k of the shift cipher is an integer 0 \leq k < n. The key k = 0 has no effect on either the plaintext or the ciphertext. INPUT: - K -- a key for this shift cipher. This must be an integer k such that 0 \leq k < n, where n 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 24 sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: key = S.random_key(); key  # random 12 sage: S.inverse_key(key)         # random 4 sage: S = ShiftCryptosystem(BinaryStrings()) sage: key = S.random_key(); key  # random 1 sage: S.inverse_key(key)         # random 1 sage: key = S.random_key(); key  # random 0 sage: S.inverse_key(key)         # random 0 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 """ # Let A be the alphabet of this cryptosystem and let n be the number # of elements in A. If k is a key, then the corresponding inverse # key is -k mod n. return self.key_space()(-Integer(K)).lift() def random_key(self): r""" Generate a random key within the key space of this shift cipher. The generated key is an integer 0 \leq k < n with n being the size of the cipher domain. Thus there are n possible keys in the key space, which is the set \ZZ / n\ZZ. The key k = 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 """ # Return a random element in ZZ/nZZ where n is the number of elements # in the plaintext/ciphertext alphabet and key space. from sage.misc.prandom import randint return Integer(randint(0, self.alphabet_size() - 1)) class SubstitutionCryptosystem(SymmetricKeyCryptosystem): """ Create a substitution cryptosystem. ABCDEFGHIJKLMN sage: e = E(K) sage: e ABCDEFGHIJKLMN Cipher on Free alphabetic string monoid on A-Z sage: e(S("THECATINTHEHAT")) TIGFEYOUBQOSMG def __init__(self, S, n): """ See VigenereCryptosystem for full documentation. EXAMPLES:: sage: S = AlphabeticStrings() ABCDEFGHIJKLMN sage: e = E(K) sage: e ABCDEFGHIJKLMN Cipher on Free alphabetic string monoid on A-Z sage: e(S("THECATINTHEHAT")) TIGFEYOUBQOSMG """ """ e = self(K) return e(M)
• ## sage/crypto/classical_cipher.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/classical_cipher.py
 a """ Classical Ciphers. Classical Ciphers """ #***************************************************************************** def __init__(self, parent, key): """ Create a Hill cipher. INPUT: Parent and key EXAMPLES:: sage: S = AlphabeticStrings() sage: E = HillCryptosystem(S,3) sage: E [1 0 1] [0 1 1] [2 2 3] sage: e = E(A) sage: e [1 0 1] [0 1 1] [2 2 3] sage: e = E(A); e Hill cipher on Free alphabetic string monoid on A-Z of block length 3 sage: e(S("LAMAISONBLANCHE")) JYVKSKQPELAYKPV TESTS:: sage: S = AlphabeticStrings() sage: E = HillCryptosystem(S,3) sage: E == loads(dumps(E)) if len(M) % m != 0: raise TypeError, "The length of M (= %s) must be a multiple of %s." % (M, m ) Alph = list(S.alphabet()) A = self.key() # A is an m x m matrix A = self.key() # A is an m x m matrix R = A.parent().base_ring() V = FreeModule(R,m) Mstr = str(M) v = V([ Alph.index(Mstr[m*i+j]) for j in range(m) ]) C += (v * A).list() return S([ k.lift() for k in C ]) def _repr_(self): r""" Return the string representation of this Hill cipher. EXAMPLES:: sage: H = HillCryptosystem(AlphabeticStrings(), 3) sage: M = MatrixSpace(IntegerModRing(26), 3, 3) sage: A = M([[1,0,1], [0,1,1], [2,2,3]]) sage: e = H(A); e Hill cipher on Free alphabetic string monoid on A-Z of block length 3 """ return "Hill cipher on %s of block length %s" % ( self.parent().cipher_domain(), self.parent().block_length() ) def inverse(self): E = self.parent() try: raise ValueError, "Argument\n\n%s\n\nmust be an invertible cipher." % self return E(B) class ShiftCipher(SymmetricKeyCipher): r""" Shift cipher class. This is the class that does the actual work of encryption and decryption. Users should not directly instantiate or create objects of this class. Instead, functionalities of this class should be accessed via :class:ShiftCryptosystem  as the latter provides a convenient user interface. """ def __init__(self, parent, key): r""" Create a shift cipher. INPUT: - parent -- a ShiftCryptosystem object. - key -- a secret key. EXAMPLES:: 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 """ SymmetricKeyCipher.__init__(self, parent, key) def __eq__(self, other): r""" Comparing this ShiftCipher with other. Two ShiftCipher objects are the same if they are of the same type, have the same parent, and share the same secret key. INPUT: - other -- another object to compare with. OUTPUT: - True if self and other are the same ShiftCipher object; False otherwise. EXAMPLES:: sage: shift1 = ShiftCryptosystem(AlphabeticStrings()) sage: shift2 = ShiftCryptosystem(AlphabeticStrings()) sage: shift1 == shift2 True sage: shift1 = ShiftCryptosystem(HexadecimalStrings()) sage: shift2 = ShiftCryptosystem(BinaryStrings()) sage: shift1 == shift2 False """ return type(self) == type(other) and self.parent() == other.parent() and self.key() == other.key() def __call__(self, M): r""" Return the ciphertext (respectively, plaintext) corresponding to M. This is the main place where encryption and decryption takes place. INPUT: - M -- a message to be encrypted or decrypted. This message must be encoded using the plaintext or ciphertext alphabet. The current behaviour is that the plaintext and ciphertext alphabets are the same alphabet. OUTPUT: - The ciphertext or plaintext corresponding to M. EXAMPLES: These are indirect doctests. Functionalities of this class are usually invoked from :class:ShiftCryptosystem . :: sage: S = ShiftCryptosystem(AlphabeticStrings()) sage: S.enciphering(12, S.encoding("Stop shifting me.")) EFABETURFUZSYQ sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: S.enciphering(7, S.encoding("Shift me now.")) cadfd0ddeb97d4dc97d5d6ee95 sage: S = ShiftCryptosystem(BinaryStrings()) sage: S.enciphering(1, S.encoding("OK, enough shifting.")) 1011000010110100110100111101111110011010100100011001000010001010100110001001011111011111100011001001011110010110100110011000101110010110100100011001100011010001 """ dom = self.domain()  # = plaintext_space = ciphertext_space if not isinstance(M, StringMonoidElement) and M.parent() == dom: raise TypeError("Argument M (= %s) must be a string in the plaintext/ciphertext space." % M) from sage.rings.integer_mod import Mod A = list(dom.alphabet())   # plaintext/ciphertext alphabet as a list N = self.domain().ngens()  # number of elements in this alphabet K = self.key()             # encryption/decryption key # Here, M is a message encoded within the ciphertext/plaintext # alphabet of this shift cryptosystem. The list A above is a list of # all elements of this alphabet, each element being associated with # an index 0 <= i < n, where n is the size of A. Now get the index # of each character in the message M, storing all these indices # in the index list I. # TODO: the following two lines are coded with clarity and # readability in mind. It is possible to remove the overhead of # doing a list comprehension to get the index associated with each # element of M. For example, the next two lines can be crammed into # one list comprehension as follows: # return dom([ A.index(A[Mod(A.index(str(i)) + K, N).lift()]) for i in M ]) # But it performs badly compared to the following two lines. I = [A.index(str(e)) for e in M] # Perform encryption/decryption on the whole message M, returning # the result as a string encoded in the alphabet A. return dom([ A.index(A[Mod(i + K, N).lift()]) for i in I ]) def _repr_(self): r""" Return the string representation of this shift cipher. EXAMPLES:: sage: S = ShiftCryptosystem(AlphabeticStrings()) sage: S(3) Shift cipher on Free alphabetic string monoid on A-Z sage: S = ShiftCryptosystem(HexadecimalStrings()) sage: S(5) Shift cipher on Free hexadecimal string monoid sage: S = ShiftCryptosystem(BinaryStrings()) sage: S(1) Shift cipher on Free binary string monoid """ # The shift cipher has the plaintext and ciphertext spaces defined # over the same non-empty alphabet. The cipher domain is the same # as the alphabet used for the plaintext and ciphertext spaces. return "Shift cipher on %s" % self.parent().cipher_domain() class SubstitutionCipher(SymmetricKeyCipher): """ Substitution cipher class def __init__(self, parent, key): """ Create a substitution cipher. INPUT: Parent and key EXAMPLES:: sage: S = AlphabeticStrings() sage: E = SubstitutionCryptosystem(S) sage: E sage: m = S("THECATINTHEHAT") sage: e(m) GSVXZGRMGSVSZG TESTS:: sage: S = AlphabeticStrings() sage: E = SubstitutionCryptosystem(S) sage: E == loads(dumps(E)) I = [ A.index(K[i]) for i in range(len(K)) ] Mstr = str(M) return S([ I[A.index(Mstr[i])] for i in range(len(Mstr)) ]) def _repr_(self): r""" Return the string representation of this substitution cipher. EXAMPLES:: sage: A = HexadecimalStrings(); S = SubstitutionCryptosystem(A) sage: K = A([16-i for i in xrange(16)]); S(K) Substitution cipher on Free hexadecimal string monoid sage: A = AlphabeticStrings(); S = SubstitutionCryptosystem(A) sage: K = A([26-i for i in xrange(26)]); S(K) Substitution cipher on Free alphabetic string monoid on A-Z sage: A = OctalStrings(); S = SubstitutionCryptosystem(A) sage: K = A([8-i for i in xrange(8)]); S(K) Substitution cipher on Free octal string monoid sage: A = BinaryStrings(); S = SubstitutionCryptosystem(A) sage: K = A([2-i for i in xrange(2)]); S(K) Substitution cipher on Free binary string monoid sage: A = Radix64Strings(); S = SubstitutionCryptosystem(A) sage: K = A([64-i for i in xrange(64)]); S(K) Substitution cipher on Free radix 64 string monoid """ return "Substitution cipher on %s" % self.parent().cipher_domain() def inverse(self): E = self.parent() K = E.inverse_key(self.key()) def __init__(self, parent, key): """ Create a transposition cipher. INPUT: Parent and key EXAMPLES:: sage: S = AlphabeticStrings() sage: E = TranspositionCryptosystem(S,14) sage: E sage: m = S("THECATINTHEHAT") sage: e(m) TAHEHTNITACEHT EXAMPLES:: sage: S = AlphabeticStrings() sage: E = TranspositionCryptosystem(S,15); sage: m = S("THECATANDTHEHAT") sage: G = E.key_space() sage: G Symmetric group of order 15! as a permutation group sage: g = G([ 3, 2, 1, 6, 5, 4, 9, 8, 7, 12, 11, 10, 15, 14, 13 ]) sage: g = G([ 3, 2, 1, 6, 5, 4, 9, 8, 7, 12, 11, 10, 15, 14, 13 ]) sage: e = E(g) sage: e(m) EHTTACDNAEHTTAH TESTS:: sage: S = AlphabeticStrings() sage: E = TranspositionCryptosystem(S,14) sage: E == loads(dumps(E)) raise TypeError, "Argument M (= %s) must be a string in the plaintext space." % M if not mode == "ECB": raise NotImplementedError, "Enciphering not implemented for mode (= %s) other than 'ECB'." % mode g = self.key() g = self.key() N = len(M) m = self.parent().block_length() if not N%m == 0: def __init__(self, parent, key): """ Create a Vigenere cipher. INPUT: Parent and key EXAMPLES:: sage: S = AlphabeticStrings() sage: E = VigenereCryptosystem(S,11) sage: K = S("SHAKESPEARE") sage: m = S("THECATINTHEHAT") sage: e(m) LOEMELXRTYIZHT TESTS:: sage: S = AlphabeticStrings() sage: E = VigenereCryptosystem(S,11) sage: E == loads(dumps(E)) raise TypeError, "Argument M (= %s) must be a string in the plaintext space." % M if not mode == "ECB": raise NotImplementedError, "Enciphering not implemented for mode (= %s) other than 'ECB'." % mode K = self.key() K = self.key() m = self.parent().period() n = S.ngens() # This uses the internal structure of string monoids Melt = M._element_list Kelt = K._element_list Melt = M._element_list Kelt = K._element_list return S([ (Melt[i]+Kelt[i%m])%n for i in range(len(M)) ]) def inverse(self):
• ## sage/crypto/cryptosystem.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/cryptosystem.py
 a """ Cryptosystems. r""" 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 :class:HillCryptosystem . It is a symmetric key cipher so :class:HillCryptosystem  is a child class of :class:SymmetricKeyCryptosystem , which in turn is a child class of :class:Cryptosystem . The following diagram shows the inheritance relationship of particular cryptosystems:: Cryptosystem + SymmetricKeyCryptosystem | + HillCryptosystem | + LFSRCryptosystem | + ShiftCryptosystem | + ShrinkingGeneratorCryptosystem | + SubstitutionCryptosystem | + TranspositionCryptosystem | + VigenereCryptosystem + PublicKeyCryptosystem """ #***************************************************************************** from sage.sets.set import Set_generic class Cryptosystem(parent_old.Parent, Set_generic): r""" A cryptosystem is a pair of maps .. math:: E : {\mathcal K} \rightarrow {\rm Hom}({\mathcal M},{\mathcal C}) .. math:: D : {\mathcal K} \rightarrow {\rm Hom}({\mathcal C},{\mathcal M}) where {\mathcal K} is the keyspace, {\mathcal M} is the plaintext or message space, and {\mathcal C} is the ciphertext space. In many instances {\mathcal M} = {\mathcal C} and the images will lie in {\rm Aut}({\mathcal M}). An element of the image of E is called a cipher. We may assume that E and D are injective, hence identify a key K in {\mathcal K} with its image E_K := E(K) in \mathrm{Hom}({\mathcal M},{\mathcal C}). The cryptosystem has the property that for every encryption key K_1 there is a decryption key K_2 such that D_{K_2} \circ E_{K_1}. A cryptosystem with the property that K := K_2 = K_1, is called a symmetric cryptosystem. Otherwise, if the key K_2 \ne K_1, nor is K_2 easily derived from K_1, we call the cryptosystem asymmetric of public key. In that case, K_1 is called the public key and K_2 is called the private key. """ def __init__(self, plaintext_space, ciphertext_space, key_space, block_length = 1, period = None): self._cipher_domain = plaintext_space self._cipher_codomain = ciphertext_space self._key_space = key_space self._block_length = block_length self._period = period r""" A base cryptosystem class. This is meant to be extended by other specialized child classes that implement specific cryptosystems. A cryptosystem is a pair of maps def __eq__(self,right): return type(self) == type(right) and  \ self._cipher_domain == right._cipher_domain and \ self._cipher_codomain == right._cipher_codomain and \ self._key_space ==  right._key_space and \ self._block_length == right._block_length and \ self._period == right._period .. math:: def plaintext_space(self): return self._cipher_domain E : {\mathcal K} \rightarrow {\rm Hom}({\mathcal M},{\mathcal C}) def cipher_domain(self): return self._cipher_domain .. math:: def ciphertext_space(self): return self._cipher_codomain D : {\mathcal K} \rightarrow {\rm Hom}({\mathcal C},{\mathcal M}) def cipher_codomain(self): return self._cipher_codomain where {\mathcal K} is the key space, {\mathcal M} is the plaintext or message space, and {\mathcal C} is the ciphertext space. In many instances {\mathcal M} = {\mathcal C} and the images will lie in {\rm Aut}({\mathcal M}). An element of the image of E is called a cipher. def key_space(self): return self._key_space We may assume that E and D are injective, hence identify a key K in {\mathcal K} with its image E_K := E(K) in \mathrm{Hom}({\mathcal M},{\mathcal C}). def block_length(self): return self._block_length The cryptosystem has the property that for every encryption key K_1 there is a decryption key K_2 such that D_{K_2} \circ E_{K_1}. A cryptosystem with the property that K := K_2 = K_1, is called a symmetric cryptosystem. Otherwise, if the key K_2 \ne K_1, nor is K_2 easily derived from K_1, we call the cryptosystem asymmetric or public key. In that case, K_1 is called the public key and K_2 is called the private key. def period(self): if self._period is None: raise TypeError, "Argument has no associated period." return self._period INPUT: - plaintext_space -- the plaintext alphabet. - ciphertext_space -- the ciphertext alphabet. - key_space -- the key alphabet. - block_length -- (default: 1) the block length. - period -- (default: None) the period. EXAMPLES: Various classical cryptosystems:: 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 """ def __init__(self, plaintext_space, ciphertext_space, key_space, block_length=1, period=None): r""" Create a Cryptosystem object. See the class Cryptosystem for detailed documentation. INPUT: - plaintext_space -- the plaintext alphabet. - ciphertext_space -- the ciphertext alphabet. - key_space -- the key alphabet. - block_length -- (default: 1) the block length. - period -- (default: None) the period. EXAMPLES: Various classical cryptosystems:: 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 """ self._cipher_domain = plaintext_space self._cipher_codomain = ciphertext_space self._key_space = key_space self._block_length = block_length self._period = period def __eq__(self, right): r""" Comparing self with right. Two Cryptosystem objects are the same if they satisfy all of these conditions: - share the same type - have the same cipher domain - have the same cipher codomain - share the same key space - share the same block length - have the same period INPUT: - right -- a Cryptosystem object. EXAMPLES: Pairs of equivalent classical cryptosystems:: sage: sub1 = SubstitutionCryptosystem(AlphabeticStrings()) sage: sub2 = SubstitutionCryptosystem(AlphabeticStrings()) sage: sub1 == sub2 True sage: shift1 = ShiftCryptosystem(HexadecimalStrings()) sage: shift2 = ShiftCryptosystem(HexadecimalStrings()) sage: shift1 == shift2 True sage: hill1 = HillCryptosystem(AlphabeticStrings(), 4) sage: hill2 = HillCryptosystem(AlphabeticStrings(), 4) sage: hill1 == hill2 True sage: tran1 = TranspositionCryptosystem(HexadecimalStrings(), 5) sage: tran2 = TranspositionCryptosystem(HexadecimalStrings(), 5) sage: tran1 == tran2 True sage: vig1 = VigenereCryptosystem(AlphabeticStrings(), 7) sage: vig2 = VigenereCryptosystem(AlphabeticStrings(), 7) sage: vig1 == vig2 True Pairs of different classical cryptosystems:: sage: sub1 = SubstitutionCryptosystem(AlphabeticStrings()) sage: sub2 = SubstitutionCryptosystem(OctalStrings()) sage: sub1 == sub2 False sage: shift1 = ShiftCryptosystem(HexadecimalStrings()) sage: shift2 = ShiftCryptosystem(BinaryStrings()) sage: shift1 == shift2 False sage: hill1 = HillCryptosystem(Radix64Strings(), 4) sage: hill2 = HillCryptosystem(Radix64Strings(), 5) sage: hill1 == hill2 False sage: tran1 = TranspositionCryptosystem(Radix64Strings(), 3) sage: tran2 = TranspositionCryptosystem(HexadecimalStrings(), 3) sage: tran1 == tran2 False sage: vig1 = VigenereCryptosystem(AlphabeticStrings(), 7) sage: vig2 = VigenereCryptosystem(Radix64Strings(), 7) sage: vig1 == vig2 False """ return type(self) == type(right) and  \ self._cipher_domain == right._cipher_domain and \ self._cipher_codomain == right._cipher_codomain and \ self._key_space ==  right._key_space and \ self._block_length == right._block_length and \ self._period == right._period def plaintext_space(self): r""" Return the plaintext alphabet of this cryptosystem. EXAMPLES: The plaintext spaces of various classical cryptosystems:: 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 """ return self._cipher_domain def cipher_domain(self): r""" Return the alphabet used by this cryptosystem for encoding plaintexts. This is the same as the plaintext 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 """ return self._cipher_domain def ciphertext_space(self): r""" 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 """ return self._cipher_codomain def cipher_codomain(self): r""" 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 """ return self._cipher_codomain def key_space(self): r""" 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 """ return self._key_space def block_length(self): r""" 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 """ return self._block_length def period(self): if self._period is None: raise TypeError, "Argument has no associated period." return self._period class SymmetricKeyCryptosystem(Cryptosystem): """ """ r""" The base class for symmetric key, or secret key, cryptosystems. """ def alphabet_size(self): r""" Return the number of elements in the alphabet of this cryptosystem. This only applies to any cryptosystem whose plaintext and ciphertext spaces are the same alphabet. EXAMPLES:: 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 """ return self._cipher_domain.ngens() class PublicKeyCryptosystem(Cryptosystem): """ """ r""" The base class for asymmetric or public-key cryptosystems. """
• ## sage/crypto/lfsr.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/lfsr.py
 a r""" Linear feedback shift register (LFSR) sequence commands. Linear feedback shift register (LFSR) sequence commands Stream ciphers have been used for a long time as a source of pseudo-random number generators. S. Golomb [G] gives a list of three statistical properties a S. Golomb [G]_ gives a list of three statistical properties a sequence of numbers {\bf a}=\{a_n\}_{n=1}^\infty, a_n\in \{0,1\}, should display to be considered "random". Define the autocorrelation of {\bf a} to be .. math:: C(k)=C(k,{\bf a})=\lim_{N\rightarrow \infty} {1\over N}\sum_{n=1}^N (-1)^{a_n+a_{n+k}}. C(k)=C(k,{\bf a})=\lim_{N\rightarrow \infty} {1\over N}\sum_{n=1}^N (-1)^{a_n+a_{n+k}}. In the case where {\bf a} is periodic with period .. math:: C(k)={1\over P}\sum_{n=1}^P (-1)^{a_n+a_{n+k}}. C(k)={1\over P}\sum_{n=1}^P (-1)^{a_n+a_{n+k}}. Assume {\bf a} is periodic with period P. -  balance: |\sum_{n=1}^P(-1)^{a_n}|\leq 1. -  low autocorrelation: .. math:: C(k)= \left\{ \begin{array}{cc} 1,& k=0,\\ \epsilon, & k\not= 0. \end{array} \right. C(k)= \left\{ \begin{array}{cc} 1,& k=0,\\ \epsilon, & k\not= 0. \end{array} \right. (For sequences satisfying these first two properties, it is known .. math:: \begin{array}{c} f(x_0,...,x_{n-1})=(x_1,x_2,...,x_n),\\ x_n=C(x_0,...,x_{n-1}), \end{array} \begin{array}{c} f(x_0,...,x_{n-1})=(x_1,x_2,...,x_n),\\ x_n=C(x_0,...,x_{n-1}), \end{array} where C:{\bf F}_q^d\rightarrow {\bf F}_q is a given .. math:: C(x_0,...,x_{n-1})=a_0x_0+...+a_{n-1}x_{n-1}, C(x_0,...,x_{n-1})=a_0x_0+...+a_{n-1}x_{n-1}, for some given constants a_i\in {\bf F}_q, the map is .. math:: f(x)=a_{{0}}+a_{{1}}x+...+a_{{n}}{x}^n+..., f(x)=a_{{0}}+a_{{1}}x+...+a_{{n}}{x}^n+..., .. math:: g(x)=b_{{0}}+b_{{1}}x+...+b_{{n}}{x}^n+..., g(x)=b_{{0}}+b_{{1}}x+...+b_{{n}}{x}^n+..., be given polynomials in {\bf F}_2[x] and let .. math:: h(x)={f(x)\over g(x)}=c_0+c_1x+...+c_nx^n+... \ . h(x)={f(x)\over g(x)}=c_0+c_1x+...+c_nx^n+... \ . We can compute a recursion formula which allows us to rapidly .. math:: c_{n}=\sum_{i=1}^n {{-b_i\over b_0}c_{n-i}}. c_{n}=\sum_{i=1}^n {{-b_i\over b_0}c_{n-i}}. .. math:: f(x)=1,\ \ \ \ g(x)=x^4+x+1, f(x)=1,\ \ \ \ g(x)=x^4+x+1, then .. math:: h(x)=1+x+x^2+x^3+x^5+x^7+x^8+...\ . h(x)=1+x+x^2+x^3+x^5+x^7+x^8+...\ . The coefficients of h are .. math:: \begin{array}{c} 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, \\ 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, ...\ . \end{array} \begin{array}{c} 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, \\ 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, ...\ . \end{array} The sequence of 0,1's is periodic with period P=2^4-1=15 and satisfies Golomb's three randomness conditions. However, this sequence of period 15 can be "cracked" (i.e., a procedure to reproduce g(x)) by knowing only 8 terms! This is the function of the Berlekamp-Massey algorithm [M], terms! This is the function of the Berlekamp-Massey algorithm [M]_, implemented as berlekamp_massey.py. [G] Solomon Golomb, Shift register sequences, Aegean Park Press, Laguna Hills, Ca, 1967 .. [G] Solomon Golomb, Shift register sequences, Aegean Park Press, Laguna Hills, Ca, 1967 [M] James L. Massey, "Shift-Register Synthesis and BCH Decoding." IEEE Trans. on Information Theory, vol. 15(1), pp. 122-127, Jan 1969. .. [M] James L. Massey, "Shift-Register Synthesis and BCH Decoding." IEEE Trans. on Information Theory, vol. 15(1), pp. 122-127, Jan 1969. AUTHORS: def lfsr_sequence(key, fill, n): r""" This function creates an lfsr sequence. INPUT: -  key - a list of finite field elements, [c_0,c_1,...,c_k]. -  fill - the list of the initial terms of the lfsr sequence, [x_0,x_1,...,x_k]. -  n - number of terms of the sequence that the function returns. OUTPUT: The lfsr sequence defined by x_{n+1} = c_kx_n+...+c_0x_{n-k}, for n \leq k. EXAMPLES:: sage: F = GF(2); l = F(1); o = F(0) sage: F = GF(2); S = LaurentSeriesRing(F,'x'); x = S.gen() sage: fill = [l,l,o,l]; key = [1,o,o,l]; n = 20 1 + x + x^4 + x^5 + x^8 + x^9 + x^12 + x^13 + x^16 + x^17 + O(x^20) sage: (1+x+x^3)/(g.reverse()+O(x^20)) 1 + x + x^3 + x^4 + x^5 + x^7 + x^8 + x^9 + x^11 + x^12 + x^13 + x^15 + x^16 + x^17 + x^19 + O(x^20) AUTHORS: - Timothy Brock (2005-11): with code modified from Python 4/15 sage: lfsr_autocorrelation(s,int(15),7) 4/15 AUTHORS: - Timothy Brock (2006-04-17) L0 = L0 + L0[:k] L1 = [int(L0[i])*int(L0[i + k])/p for i in range(_p)] return sum(L1) def lfsr_connection_polynomial(s): """ INPUT: -  s - a sequence of elements of a finite field (F) 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 [M]. article [M]_. EXAMPLE:: sage: F = GF(2) 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: berlekamp_massey(s) x^4 + x^3 + 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: 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). AUTHORS: - Timothy Brock (2006-04-17) REFERENCES: - [M] J. L. Massey, 'Shift-register synthesis and BCH decoding,' IEEE Trans. Inform. Theory, vol. IT-15, pp. 122-127, Jan. 19 69. """ # Initialization: FF = s[0].base_ring() R = PolynomialRing(FF, "x") x = R.gen() C = R(1); B = R(1); m = 1; b = FF(1); L = 0; N = 0 while N < len(s): if L > 0: r = min(L+1,C.degree()+1) B = T N += 1 return C
• ## sage/crypto/stream.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/stream.py
 a """ Stream Cryptosystems. Stream Cryptosystems """ #***************************************************************************** def __init__(self, field = None): """ Create a linear feedback shift cryptosystem. INPUT: A string monoid over a binary alphabet. OUTPUT: EXAMPLES:: sage: E = LFSRCryptosystem(FiniteField(2)) sage: E = LFSRCryptosystem(FiniteField(2)) sage: E LFSR cryptosystem over Finite Field of size 2 LFSR cryptosystem over Finite Field of size 2 TESTS:: sage: E = LFSRCryptosystem(FiniteField(2)) sage: E == loads(dumps(E)) True TODO: Implement LFSR cryptosytem for arbitrary rings. The current TODO: Implement LFSR cryptosystem for arbitrary rings. The current implementation is limited to the finite field of 2 elements only because of the dependence on binary strings. """ def __call__(self, key): """ Create a LFSR cipher. INPUT: A polynomial and initial state of the LFSR. """ """ if not isinstance(key, (list,tuple)) and len(key) == 2: raise TypeError, "Argument key (= %s) must be a list of tuple of length 2" % key poly = key[0]; IS = key[1] "The length of IS (= %s) must equal the degree of poly (= %s)" % (IS, poly) return LFSRCipher(self, poly, IS) def __repr__(self): def _repr_(self): r""" Return the string representation of this LFSR cryptosystem. EXAMPLES:: sage: LFSRCryptosystem(FiniteField(2)) LFSR cryptosystem over Finite Field of size 2 """ return "LFSR cryptosystem over %s" % self._field def encoding(self,M): def __init__(self, field = None): """ Create a shrinking generator cryptosystem. INPUT: A string monoid over a binary alphabet. OUTPUT: EXAMPLES:: sage: E = ShrinkingGeneratorCryptosystem() sage: E = ShrinkingGeneratorCryptosystem() sage: E Shrinking generator cryptosystem over Finite Field of size 2 Shrinking generator cryptosystem over Finite Field of size 2 """ if field is None: field = FiniteField(2) def __call__(self, key): """ Create a Shrinking generator cipher. INPUT: A list or tuple consisting of two LFSR ciphers (e1,e2). OUTPUT: The shrinking generator cipher with key stream generator e1 and decimating cipher e2. """ """ if not isinstance(key, (list,tuple)) and len(key) == 2: raise TypeError, "Argument key (= %s) must be a list of tuple of length 2" % key e1 = key[0]; e2 = key[1] raise TypeError, "The key (= (%s,%s)) must be a tuple of two LFSR ciphers." % key return ShrinkingGeneratorCipher(self, e1, e2) def __repr__(self): def _repr_(self): r""" Return the string representation of this shrinking generator cryptosystem. EXAMPLES:: sage: ShrinkingGeneratorCryptosystem() Shrinking generator cryptosystem over Finite Field of size 2 """ return "Shrinking generator cryptosystem over %s" % self._field def encoding(self,M): return S.encoding(M) except: raise TypeError, "Argument M = %s does not encode in the cipher domain" % M
• ## sage/crypto/stream_cipher.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/crypto/stream_cipher.py
 a def __init__(self, parent, poly, IS): """ Create a linear feedback shift register (LFSR) cipher. INPUT: -  parent - parent -  poly - connection polynomial -  IS - initial state EXAMPLES:: sage: FF = FiniteField(2) sage: P. = PolynomialRing(FF) sage: E = LFSRCryptosystem(FF) sage: e(m) 00111001110111101011111001001101110101011011101000011001100101101011001000000011100101101010111100000101110100111111101100000101110101111010111101000011 sage: m == e(e(m)) True True TESTS:: sage: FF = FiniteField(2) sage: P. = PolynomialRing(FF) sage: E = LFSRCryptosystem(FF) def __call__(self, M, mode = "ECB"): r""" Generate key stream from the binary string M. INPUT: -  M - a StringMonoidElement -  mode - ignored (default: 'ECB') EXAMPLE:: sage: k = GF(2) sage: P. = PolynomialRing( k ) sage: LFSR = LFSRCryptosystem( k ) Kelt = lfsr_sequence(poly.list(), IS, N) return B([ (Melt[i]+int(Kelt[i]))%n for i in range(N) ]) def _repr_(self): r""" Return the string representation of this LFSR cipher. EXAMPLES:: sage: FF = FiniteField(2) sage: P. = 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 """ return "LFSR cipher on %s" % self.domain() def connection_polynomial(self): """ """ The connection polynomial defining the LFSR of the cipher. EXAMPLE:: sage: k = GF(2) sage: P. = PolynomialRing( k ) sage: LFSR = LFSRCryptosystem( k ) return self.key()[0] def initial_state(self): """ """ The initial state of the LFSR cipher. EXAMPLE:: sage: k = GF(2) sage: P. = PolynomialRing( k ) sage: LFSR = LFSRCryptosystem( k ) def __init__(self, parent, e1, e2): """ Create a shrinking generator cipher. INPUT: -  parent - parent -  poly - connection polynomial -  IS - initial state EXAMPLES:: sage: FF = FiniteField(2) sage: P. = 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: FF = FiniteField(2) sage: P. = 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 ((x^7 + x + 1, [0, 1, 0, 1, 0, 0, 0]), (x^9 + x^3 + 1, [0, 0, 1, 0, 0, 0, 1, 0, 1])) Shrinking generator cipher on Free binary string monoid """ if not isinstance(e1, LFSRCipher): raise TypeError, "Argument e1 (= %s) must be a LFSR cipher." % e1 SymmetricKeyCipher.__init__(self, parent, key = (e1, e2)) def keystream_cipher(self): """ """ The LFSR cipher generating the output key stream. EXAMPLE:: sage: FF = FiniteField(2) sage: P. = 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: FF = FiniteField(2) sage: P. = 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() (x^7 + x + 1, [0, 1, 0, 1, 0, 0, 0]) LFSR cipher on Free binary string monoid """ return self.key()[0] def decimating_cipher(self): """ """ The LFSR cipher generating the decimating key stream. EXAMPLE:: sage: FF = FiniteField(2) sage: P. = 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: FF = FiniteField(2) sage: P. = 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() (x^9 + x^3 + 1, [0, 0, 1, 0, 0, 0, 1, 0, 1]) LFSR cipher on Free binary string monoid """ return self.key()[1] def __call__(self, M, mode = "ECB"): r""" INPUT: -  M - a StringMonoidElement -  mode - ignored (default: 'ECB') EXAMPLES:: sage: FF = FiniteField(2) sage: P. = 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: FF = FiniteField(2) sage: P. = 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: B = BinaryStrings() sage: m = B.encoding("THECATINTHEHAT") sage: B = BinaryStrings() sage: m = B.encoding("THECATINTHEHAT") sage: c = e(m) sage: c.decoding() "t\xb6\xc1'\x83\x17\xae\xc9ZO\x84V\x7fX" N = len(M) n = max(n1,n2) CStream = [] while k < N: while k < N: r = max(N-k,2*n) KStream = lfsr_sequence(g1.list(), IS_1, r) DStream = lfsr_sequence(g2.list(), IS_2, r) CStream.append(int(MStream[k]+KStream[i])) k += 1 if k == N: break break IS_1 = KStream[r-n-1:r-n+n1] IS_2 = DStream[r-n-1:r-n+n2] return B(CStream) def _repr_(self): r""" Return the string representation of this shrinking generator cipher. EXAMPLES:: sage: FF = FiniteField(2) sage: P. = 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)); e Shrinking generator cipher on Free binary string monoid """ return "Shrinking generator cipher on %s" % self.domain()
• ## sage/monoids/free_abelian_monoid_element.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/monoids/free_abelian_monoid_element.py
 a - David Kohel (2005-09) EXAMPLES: Recall the example from abelian monoids. EXAMPLES: Recall the example from abelian monoids. :: from sage.structure.element import MonoidElement, generic_power def is_FreeAbelianMonoidElement(x): r""" Queries whether x is an object of type FreeAbelianMonoidElement. INPUT: - x -- an object. OUTPUT: - True if x is an object of type FreeAbelianMonoidElement; False otherwise. """ return isinstance(x, FreeAbelianMonoidElement) class FreeAbelianMonoidElement(MonoidElement):
• ## sage/monoids/string_monoid.py

diff -r 7fc8b5794aca -r 5d9b9378ee4f sage/monoids/string_monoid.py