def ascii_to_bin(A): r""" Return the binary representation of the ASCII string ``A``. INPUT: - ``A`` -- a string or list of ASCII characters. OUTPUT: - The binary representation of ``A``. ALGORITHM: Let `A = a_0 a_1 \cdots a_{n-1}` be an ASCII string, where each `a_i` is an ASCII character. Let `c_i` be the ASCII integer corresponding to `a_i` and let `b_i` be the binary representation of `c_i`. The binary representation `B` of `A` is `B = b_0 b_1 \cdots b_{n-1}`. EXAMPLES: The binary representation of some ASCII strings:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin("A") 01000001 sage: ascii_to_bin("Abc123") 010000010110001001100011001100010011001000110011 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:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin("") <BLANKLINE> sage: ascii_to_bin(" ") 00100000 sage: ascii_to_bin([]) <BLANKLINE> This function also accepts a list of ASCII characters. You can also pass in a list of strings:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin(["A", "b", "c", "1", "2", "3"]) 010000010110001001100011001100010011001000110011 sage: ascii_to_bin(["A", "bc", "1", "23"]) 010000010110001001100011001100010011001000110011 TESTS: For a list of ASCII characters or strings, do not mix characters or strings with integers:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin(["A", "b", "c", 1, 2, 3]) Traceback (most recent call last): ... TypeError: sequence item 3: expected string, sage.rings.integer.Integer found sage: ascii_to_bin(["Abc", 1, 2, 3]) Traceback (most recent call last): ... TypeError: sequence item 1: expected string, sage.rings.integer.Integer found """ bin = BinaryStrings() return bin.encoding("".join(list(A)))
def encrypt(self, P, K, seed=None): r""" 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 `p` and `q` are Blum primes and `n = pq` is a public key, then ``seed`` is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. If ``seed=None``, then the function would generate its own random quadratic residue in `(\ZZ/n\ZZ)^{\ast}`. Where a value for ``seed`` is provided, it is your responsibility to ensure that the seed is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. OUTPUT: - The ciphertext resulting from encrypting ``P`` using the public key ``K``. The ciphertext `C` is of the form `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` is a sub-block of binary string and `x_{t+1}` is the result of the `t+1`-th iteration of the Blum-Blum-Shub algorithm. ALGORITHM: The Blum-Goldwasser encryption algorithm is described in Algorithm 8.56, page 309 of [MenezesEtAl1996]_. The algorithm works as follows: #. Let `n` be a public key, where `n = pq` is the product of two distinct Blum primes `p` and `q`. #. Let `k = \lfloor \log_2(n) \rfloor` and `h = \lfloor \log_2(k) \rfloor`. #. Let `m = m_1 m_2 \cdots m_t` be the message (plaintext) where each `m_i` is a binary string of length `h`. #. Choose a random seed `x_0`, which is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. That is, choose a random `r \in (\ZZ/n\ZZ)^{\ast}` and compute `x_0 = r^2 \bmod n`. #. For `i` from 1 to `t`, do: #. Let `x_i = x_{i-1}^2 \bmod n`. #. Let `p_i` be the `h` least significant bits of `x_i`. #. Let `c_i = p_i \oplus m_i`. #. Compute `x_{t+1} = x_t^2 \bmod n`. #. The ciphertext is `c = (c_1, c_2, \dots, c_t, x_{t+1})`. The value `h` in the algorithm is the sub-block length. If the binary string representing the message cannot be divided into blocks of length `h` 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 [MenezesEtAl1996]_. Here, we encrypt a binary string:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: p = 499; q = 547; n = p * q sage: P = "10011100000100001100" sage: C = bg.encrypt(P, n, seed=159201); C ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) Convert the ciphertext sub-blocks into a binary string:: sage: bin = BinaryStrings() sage: bin(flatten(C[0])) 00100000110011100100 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:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: K = 32300619509 sage: P = "Blum-Goldwasser encryption" sage: bg.encrypt(P, K) # random ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \ [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \ [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \ [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \ [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \ [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) TESTS: The plaintext cannot be an empty string. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.encrypt("", 3) Traceback (most recent call last): ... ValueError: The plaintext cannot be an empty string. """ # sanity check if P == "": raise ValueError("The plaintext cannot be an empty string.") n = K k = floor(log(n, base=2)) h = floor(log(k, base=2)) bin = BinaryStrings() M = "" try: # the plaintext is a binary string M = bin(P) except TypeError: # encode the plaintext as a binary string # An exception might be raised here if P cannot be encoded as a # binary string. M = bin.encoding(P) # the number of plaintext sub-blocks; each sub-block has length h t = 0 try: # Attempt to use t and h values from the algorithm described # in [MenezesEtAl1996]. t = len(M) / h # If the following raises an exception, then we can't use # the t and h values specified by [MenezesEtAl1996]. mod(len(M), t) # fall back to using other sub-block lengths except TypeError: # sub-blocks of length h = 16 if mod(len(M), 16) == 0: h = 16 t = len(M) // h # sub-blocks of length h = 8 elif mod(len(M), 8) == 0: h = 8 t = len(M) // h # sub-blocks of length h = 4 elif mod(len(M), 4) == 0: h = 4 t = len(M) // h # sub-blocks of length h = 2 elif mod(len(M), 2) == 0: h = 2 t = len(M) // h # sub-blocks of length h = 1 else: h = 1 t = len(M) // h # If no seed is provided, select a random seed. x0 = seed if seed is None: zmod = IntegerModRing(n) # K = n = pq r = zmod.random_element().lift() while gcd(r, n) != 1: r = zmod.random_element().lift() x0 = power_mod(r, 2, n) # perform the encryption to_int = lambda x: int(str(x)) C = [] for i in xrange(t): x1 = power_mod(x0, 2, n) p = least_significant_bits(x1, h) # xor p with a sub-block of length h. There are t sub-blocks of # length h each. C.append(map(xor, p, map(to_int, M[i*h : (i+1)*h]))) x0 = x1 x1 = power_mod(x0, 2, n) return (C, x1)
def encrypt(self, P, K, seed=None): r""" 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 `p` and `q` are Blum primes and `n = pq` is a public key, then ``seed`` is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. If ``seed=None``, then the function would generate its own random quadratic residue in `(\ZZ/n\ZZ)^{\ast}`. Where a value for ``seed`` is provided, it is your responsibility to ensure that the seed is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. OUTPUT: - The ciphertext resulting from encrypting ``P`` using the public key ``K``. The ciphertext `C` is of the form `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` is a sub-block of binary string and `x_{t+1}` is the result of the `t+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: #. Let `n` be a public key, where `n = pq` is the product of two distinct Blum primes `p` and `q`. #. Let `k = \lfloor \log_2(n) \rfloor` and `h = \lfloor \log_2(k) \rfloor`. #. Let `m = m_1 m_2 \cdots m_t` be the message (plaintext) where each `m_i` is a binary string of length `h`. #. Choose a random seed `x_0`, which is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. That is, choose a random `r \in (\ZZ/n\ZZ)^{\ast}` and compute `x_0 = r^2 \bmod n`. #. For `i` from 1 to `t`, do: #. Let `x_i = x_{i-1}^2 \bmod n`. #. Let `p_i` be the `h` least significant bits of `x_i`. #. Let `c_i = p_i \oplus m_i`. #. Compute `x_{t+1} = x_t^2 \bmod n`. #. The ciphertext is `c = (c_1, c_2, \dots, c_t, x_{t+1})`. The value `h` in the algorithm is the sub-block length. If the binary string representing the message cannot be divided into blocks of length `h` 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:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: p = 499; q = 547; n = p * q sage: P = "10011100000100001100" sage: C = bg.encrypt(P, n, seed=159201); C ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) Convert the ciphertext sub-blocks into a binary string:: sage: bin = BinaryStrings() sage: bin(flatten(C[0])) 00100000110011100100 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:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: K = 32300619509 sage: P = "Blum-Goldwasser encryption" sage: bg.encrypt(P, K) # random ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \ [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \ [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \ [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \ [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \ [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) TESTS: The plaintext cannot be an empty string. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.encrypt("", 3) Traceback (most recent call last): ... ValueError: The plaintext cannot be an empty string. """ # sanity check if P == "": raise ValueError("The plaintext cannot be an empty string.") n = K k = floor(log(n, base=2)) h = floor(log(k, base=2)) bin = BinaryStrings() M = "" try: # the plaintext is a binary string M = bin(P) except TypeError: # encode the plaintext as a binary string # An exception might be raised here if P cannot be encoded as a # binary string. M = bin.encoding(P) # the number of plaintext sub-blocks; each sub-block has length h t = 0 try: # Attempt to use t and h values from the algorithm described # in [MvOV1996]. t = len(M) / h # If the following raises an exception, then we can't use # the t and h values specified by [MvOV1996]. mod(len(M), t) # fall back to using other sub-block lengths except TypeError: # sub-blocks of length h = 16 if mod(len(M), 16) == 0: h = 16 t = len(M) // h # sub-blocks of length h = 8 elif mod(len(M), 8) == 0: h = 8 t = len(M) // h # sub-blocks of length h = 4 elif mod(len(M), 4) == 0: h = 4 t = len(M) // h # sub-blocks of length h = 2 elif mod(len(M), 2) == 0: h = 2 t = len(M) // h # sub-blocks of length h = 1 else: h = 1 t = len(M) // h # If no seed is provided, select a random seed. x0 = seed if seed is None: zmod = IntegerModRing(n) # K = n = pq r = zmod.random_element().lift() while gcd(r, n) != 1: r = zmod.random_element().lift() x0 = power_mod(r, 2, n) # perform the encryption to_int = lambda x: int(str(x)) C = [] for i in range(t): x1 = power_mod(x0, 2, n) p = least_significant_bits(x1, h) # xor p with a sub-block of length h. There are t sub-blocks of # length h each. C.append(list(map(xor, p, [to_int(_) for _ in M[i*h : (i+1)*h]]))) x0 = x1 x1 = power_mod(x0, 2, n) return (C, x1)
def ascii_to_bin(A): r""" Return the binary representation of the ASCII string ``A``. INPUT: - ``A`` -- a string or list of ASCII characters. OUTPUT: - The binary representation of ``A``. ALGORITHM: Let `A = a_0 a_1 \cdots a_{n-1}` be an ASCII string, where each `a_i` is an ASCII character. Let `c_i` be the ASCII integer corresponding to `a_i` and let `b_i` be the binary representation of `c_i`. The binary representation `B` of `A` is `B = b_0 b_1 \cdots b_{n-1}`. EXAMPLES: The binary representation of some ASCII strings:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin("A") 01000001 sage: ascii_to_bin("Abc123") 010000010110001001100011001100010011001000110011 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:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin("") <BLANKLINE> sage: ascii_to_bin(" ") 00100000 sage: ascii_to_bin([]) <BLANKLINE> This function also accepts a list of ASCII characters. You can also pass in a list of strings:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin(["A", "b", "c", "1", "2", "3"]) 010000010110001001100011001100010011001000110011 sage: ascii_to_bin(["A", "bc", "1", "23"]) 010000010110001001100011001100010011001000110011 TESTS: For a list of ASCII characters or strings, do not mix characters or strings with integers:: sage: from sage.crypto.util import ascii_to_bin sage: ascii_to_bin(["A", "b", "c", 1, 2, 3]) Traceback (most recent call last): ... TypeError: sequence item 3: expected str..., sage.rings.integer.Integer found sage: ascii_to_bin(["Abc", 1, 2, 3]) Traceback (most recent call last): ... TypeError: sequence item 1: expected str..., sage.rings.integer.Integer found """ bin = BinaryStrings() return bin.encoding("".join(list(A)))