def public_key(self, p, q): r""" 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 `n = pq`. Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a positive prime. Then `p` is a Blum prime if `p` is congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{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: ....: q = random_blum_prime(10**4, 10**5) ... sage: n = bg.public_key(p, q) sage: f = factor(n) sage: is_blum_prime(f[0][0]); is_blum_prime(f[1][0]) True True sage: p * q == f[0][0] * f[1][0] True TESTS: The input ``p`` and ``q`` must be distinct Blum primes. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.public_key(3, 3) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: bg.public_key(23, 29) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ if p == q: raise ValueError("p and q must be distinct Blum primes.") if is_blum_prime(p) and is_blum_prime(q): return p * q else: raise ValueError("p and q must be distinct Blum primes.")
def private_key(self, p, q): r""" 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 `(p, q, a, b)` where `\gcd(p, q) = ap + bq = 1`. Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a positive prime. Then `p` is a Blum prime if `p` is congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{4}`. EXAMPLES: Obtain two distinct Blum primes and compute the Blum-Goldwasser private 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.private_key(19, 23) (19, 23, -6, 5) Choose two distinct random Blum primes, compute the Blum-Goldwasser private key corresponding to those two primes, and test that the resulting private key `(p, q, a, b)` satisfies `\gcd(p, q) = ap + bq = 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 TESTS: Both of the input ``p`` and ``q`` must be distinct Blum primes. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.private_key(78307, 78307) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: bg.private_key(7, 4) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ if p == q: raise ValueError("p and q must be distinct Blum primes.") if is_blum_prime(p) and is_blum_prime(q): # here gcd(p, q) = ap + bq = 1 bezout = xgcd(p, q) a = bezout[1] b = bezout[2] return (p, q, a, b) else: raise ValueError("p and q must be distinct Blum primes.")
def decrypt(self, C, K): r""" 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 algorithm. The ciphertext `C` must be 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. - ``K`` -- a private key `(p, q, a, b)` where `p` and `q` are distinct Blum primes and `\gcd(p, q) = ap + bq = 1`. OUTPUT: - The plaintext resulting from decrypting the ciphertext ``C`` using the Blum-Goldwasser decryption algorithm. ALGORITHM: The Blum-Goldwasser decryption algorithm is described in Algorithm 8.56, page 309 of [MvOV1996]_. The algorithm works as follows: #. Let `C` be the ciphertext `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Then `t` is the number of ciphertext sub-blocks and `h` is the length of each binary string sub-block `c_i`. #. Let `(p, q, a, b)` be the private key whose corresponding public key is `n = pq`. Note that `\gcd(p, q) = ap + bq = 1`. #. Compute `d_1 = ((p + 1) / 4)^{t+1} \bmod{(p - 1)}`. #. Compute `d_2 = ((q + 1) / 4)^{t+1} \bmod{(q - 1)}`. #. Let `u = x_{t+1}^{d_1} \bmod p`. #. Let `v = x_{t+1}^{d_2} \bmod q`. #. Compute `x_0 = vap + ubq \bmod n`. #. For `i` from 1 to `t`, do: #. Compute `x_i = x_{t-1}^2 \bmod n`. #. Let `p_i` be the `h` least significant bits of `x_i`. #. Compute `m_i = p_i \oplus c_i`. #. The plaintext is `m = m_1 m_2 \cdots m_t`. EXAMPLES: The following decryption example is taken from Example 8.57, pages 309--310 of [MvOV1996]_. Here we decrypt a binary string:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: p = 499; q = 547 sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) sage: K = bg.private_key(p, q); K (499, 547, -57, 52) sage: P = bg.decrypt(C, K); P [[1, 0, 0, 1], [1, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]] Convert the plaintext sub-blocks into a binary string:: sage: bin = BinaryStrings() sage: bin(flatten(P)) 10011100000100001100 Decrypt a longer ciphertext and convert the resulting plaintext into an ASCII string:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import bin_to_ascii sage: bg = BlumGoldwasser() sage: p = 78307; q = 412487 sage: K = bg.private_key(p, q) sage: C = ([[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) sage: P = bg.decrypt(C, K) sage: bin_to_ascii(flatten(P)) 'Blum-Goldwasser encryption' TESTS: The private key `K = (p, q, a, b)` must be such that `p` and `q` are distinct Blum primes. Even if `p` and `q` pass this criterion, they must also satisfy the requirement `\gcd(p, q) = ap + bq = 1`. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) sage: K = (7, 7, 1, 2) sage: bg.decrypt(C, K) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: K = (7, 23, 1, 2) sage: bg.decrypt(C, K) Traceback (most recent call last): ... ValueError: a and b must satisfy gcd(p, q) = ap + bq = 1. sage: K = (11, 29, 8, -3) sage: bg.decrypt(C, K) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ # ciphertext c = C[0] xt1 = C[-1] # number of ciphertext sub-blocks t = len(c) # length of each ciphertext sub-block h = len(c[0]) # private key p, q, a, b = K # public key n = p * q # sanity checks if p == q: raise ValueError("p and q must be distinct Blum primes.") if (a*p + b*q) != 1: raise ValueError("a and b must satisfy gcd(p, q) = ap + bq = 1.") if (not is_blum_prime(p)) or (not is_blum_prime(q)): raise ValueError("p and q must be distinct Blum primes.") # prepare to decrypt d1 = power_mod((p + 1) // 4, t + 1, p - 1) d2 = power_mod((q + 1) // 4, t + 1, q - 1) u = power_mod(xt1, d1, p) v = power_mod(xt1, d2, q) x0 = mod(v*a*p + u*b*q, n).lift() # perform the decryption M = [] for i in range(t): x1 = power_mod(x0, 2, n) p = least_significant_bits(x1, h) M.append(list(map(xor, p, c[i]))) x0 = x1 return M
def public_key(self, p, q): r""" 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 `n = pq`. Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a positive prime. Then `p` is a Blum prime if `p` is congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{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: ... q = random_blum_prime(10**4, 10**5) ... sage: n = bg.public_key(p, q) sage: f = factor(n) sage: is_blum_prime(f[0][0]); is_blum_prime(f[1][0]) True True sage: p * q == f[0][0] * f[1][0] True TESTS: The input ``p`` and ``q`` must be distinct Blum primes. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.public_key(3, 3) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: bg.public_key(23, 29) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ if p == q: raise ValueError("p and q must be distinct Blum primes.") if is_blum_prime(p) and is_blum_prime(q): return p * q else: raise ValueError("p and q must be distinct Blum primes.")
def private_key(self, p, q): r""" 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 `(p, q, a, b)` where `\gcd(p, q) = ap + bq = 1`. Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a positive prime. Then `p` is a Blum prime if `p` is congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{4}`. EXAMPLES: Obtain two distinct Blum primes and compute the Blum-Goldwasser private 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.private_key(19, 23) (19, 23, -6, 5) Choose two distinct random Blum primes, compute the Blum-Goldwasser private key corresponding to those two primes, and test that the resulting private key `(p, q, a, b)` satisfies `\gcd(p, q) = ap + bq = 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 TESTS: Both of the input ``p`` and ``q`` must be distinct Blum primes. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.private_key(78307, 78307) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: bg.private_key(7, 4) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ if p == q: raise ValueError("p and q must be distinct Blum primes.") if is_blum_prime(p) and is_blum_prime(q): # here gcd(p, q) = ap + bq = 1 bezout = xgcd(p, q) a = bezout[1] b = bezout[2] return (p, q, a, b) else: raise ValueError("p and q must be distinct Blum primes.")
def decrypt(self, C, K): r""" 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 algorithm. The ciphertext `C` must be 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. - ``K`` -- a private key `(p, q, a, b)` where `p` and `q` are distinct Blum primes and `\gcd(p, q) = ap + bq = 1`. OUTPUT: - The plaintext resulting from decrypting the ciphertext ``C`` using the Blum-Goldwasser decryption algorithm. ALGORITHM: The Blum-Goldwasser decryption algorithm is described in Algorithm 8.56, page 309 of [MenezesEtAl1996]_. The algorithm works as follows: #. Let `C` be the ciphertext `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Then `t` is the number of ciphertext sub-blocks and `h` is the length of each binary string sub-block `c_i`. #. Let `(p, q, a, b)` be the private key whose corresponding public key is `n = pq`. Note that `\gcd(p, q) = ap + bq = 1`. #. Compute `d_1 = ((p + 1) / 4)^{t+1} \bmod{(p - 1)}`. #. Compute `d_2 = ((q + 1) / 4)^{t+1} \bmod{(q - 1)}`. #. Let `u = x_{t+1}^{d_1} \bmod p`. #. Let `v = x_{t+1}^{d_2} \bmod q`. #. Compute `x_0 = vap + ubq \bmod n`. #. For `i` from 1 to `t`, do: #. Compute `x_i = x_{t-1}^2 \bmod n`. #. Let `p_i` be the `h` least significant bits of `x_i`. #. Compute `m_i = p_i \oplus c_i`. #. The plaintext is `m = m_1 m_2 \cdots m_t`. EXAMPLES: The following decryption example is taken from Example 8.57, pages 309--310 of [MenezesEtAl1996]_. Here we decrypt a binary string:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: p = 499; q = 547 sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) sage: K = bg.private_key(p, q); K (499, 547, -57, 52) sage: P = bg.decrypt(C, K); P [[1, 0, 0, 1], [1, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]] Convert the plaintext sub-blocks into a binary string:: sage: bin = BinaryStrings() sage: bin(flatten(P)) 10011100000100001100 Decrypt a longer ciphertext and convert the resulting plaintext into an ASCII string:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: from sage.crypto.util import bin_to_ascii sage: bg = BlumGoldwasser() sage: p = 78307; q = 412487 sage: K = bg.private_key(p, q) sage: C = ([[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) sage: P = bg.decrypt(C, K) sage: bin_to_ascii(flatten(P)) 'Blum-Goldwasser encryption' TESTS: The private key `K = (p, q, a, b)` must be such that `p` and `q` are distinct Blum primes. Even if `p` and `q` pass this criterion, they must also satisfy the requirement `\gcd(p, q) = ap + bq = 1`. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) sage: K = (7, 7, 1, 2) sage: bg.decrypt(C, K) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. sage: K = (7, 23, 1, 2) sage: bg.decrypt(C, K) Traceback (most recent call last): ... ValueError: a and b must satisfy gcd(p, q) = ap + bq = 1. sage: K = (11, 29, 8, -3) sage: bg.decrypt(C, K) Traceback (most recent call last): ... ValueError: p and q must be distinct Blum primes. """ # ciphertext c = C[0] xt1 = C[-1] # number of ciphertext sub-blocks t = len(c) # length of each ciphertext sub-block h = len(c[0]) # private key p, q, a, b = K # public key n = p * q # sanity checks if p == q: raise ValueError("p and q must be distinct Blum primes.") if (a * p + b * q) != 1: raise ValueError("a and b must satisfy gcd(p, q) = ap + bq = 1.") if (not is_blum_prime(p)) or (not is_blum_prime(q)): raise ValueError("p and q must be distinct Blum primes.") # prepare to decrypt d1 = power_mod((p + 1) // 4, t + 1, p - 1) d2 = power_mod((q + 1) // 4, t + 1, q - 1) u = power_mod(xt1, d1, p) v = power_mod(xt1, d2, q) x0 = mod(v * a * p + u * b * q, n).lift() # perform the decryption M = [] for i in xrange(t): x1 = power_mod(x0, 2, n) p = least_significant_bits(x1, h) M.append(list(map(xor, p, c[i]))) x0 = x1 return M