|
3 | 3 | import random |
4 | 4 |
|
5 | 5 |
|
6 | | -def pollards_rho_discrete_log(g: int, h: int, p: int) -> Optional[int]: |
| 6 | +def pollards_rho_discrete_log(base: int, target: int, modulus: int) -> Optional[int]: |
7 | 7 | """ |
8 | | - Solve for x in the discrete logarithm problem: g^x ≡ h (mod p) |
| 8 | + Solve for x in the discrete logarithm problem: base^x ≡ target (mod modulus) |
9 | 9 | using Pollard's Rho algorithm. |
10 | 10 |
|
11 | | - This is a probabilistic algorithm that finds discrete logarithms in O(√p) time. |
| 11 | + This is a probabilistic algorithm that finds discrete logarithms in O(√modulus) time. |
12 | 12 | The algorithm may not always find a solution in a single run due to its |
13 | 13 | probabilistic nature, but it will find the correct answer when it succeeds. |
14 | 14 | |
15 | 15 | More info: https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm_for_logarithms |
16 | 16 |
|
17 | 17 | Parameters |
18 | 18 | ---------- |
19 | | - g : int |
20 | | - The generator (base). |
21 | | - h : int |
22 | | - The result value (h ≡ g^x mod p). |
23 | | - p : int |
| 19 | + base : int |
| 20 | + The generator (base of the exponential). |
| 21 | + target : int |
| 22 | + The target value (target ≡ base^x mod modulus). |
| 23 | + modulus : int |
24 | 24 | A prime modulus. |
25 | 25 |
|
26 | 26 | Returns |
@@ -48,64 +48,88 @@ def pollards_rho_discrete_log(g: int, h: int, p: int) -> Optional[int]: |
48 | 48 | True |
49 | 49 | """ |
50 | 50 |
|
51 | | - def f(x, a, b): |
52 | | - """Pseudo-random function that partitions the search space into 3 sets.""" |
53 | | - if x % 3 == 0: |
54 | | - # Multiply by g |
55 | | - return (x * g) % p, (a + 1) % (p - 1), b |
56 | | - elif x % 3 == 1: |
| 51 | + def pseudo_random_function( |
| 52 | + current_value: int, exponent_base: int, exponent_target: int |
| 53 | + ) -> tuple[int, int, int]: |
| 54 | + """ |
| 55 | + Pseudo-random function that partitions the search space into 3 sets. |
| 56 | + |
| 57 | + Returns a tuple of (new_value, new_exponent_base, new_exponent_target). |
| 58 | + """ |
| 59 | + if current_value % 3 == 0: |
| 60 | + # Multiply by base |
| 61 | + return ( |
| 62 | + (current_value * base) % modulus, |
| 63 | + (exponent_base + 1) % (modulus - 1), |
| 64 | + exponent_target, |
| 65 | + ) |
| 66 | + elif current_value % 3 == 1: |
57 | 67 | # Square |
58 | | - return (x * x) % p, (2 * a) % (p - 1), (2 * b) % (p - 1) |
| 68 | + return ( |
| 69 | + (current_value * current_value) % modulus, |
| 70 | + (2 * exponent_base) % (modulus - 1), |
| 71 | + (2 * exponent_target) % (modulus - 1), |
| 72 | + ) |
59 | 73 | else: |
60 | | - # Multiply by h |
61 | | - return (x * h) % p, a, (b + 1) % (p - 1) |
| 74 | + # Multiply by target |
| 75 | + return ( |
| 76 | + (current_value * target) % modulus, |
| 77 | + exponent_base, |
| 78 | + (exponent_target + 1) % (modulus - 1), |
| 79 | + ) |
62 | 80 |
|
63 | 81 | # Try multiple random starting points to avoid immediate collisions |
64 | 82 | max_attempts = 50 # Increased attempts for better reliability |
65 | 83 |
|
66 | 84 | for attempt in range(max_attempts): |
67 | 85 | # Use different starting values to avoid trivial collisions |
68 | | - # x represents g^a * h^b |
| 86 | + # current_value represents base^exponent_base * target^exponent_target |
69 | 87 | random.seed() # Ensure truly random values |
70 | | - a = random.randint(0, p - 2) |
71 | | - b = random.randint(0, p - 2) |
| 88 | + exponent_base = random.randint(0, modulus - 2) |
| 89 | + exponent_target = random.randint(0, modulus - 2) |
72 | 90 |
|
73 | | - # Ensure x = g^a * h^b mod p |
74 | | - x = (pow(g, a, p) * pow(h, b, p)) % p |
| 91 | + # Ensure current_value = base^exponent_base * target^exponent_target mod modulus |
| 92 | + current_value = (pow(base, exponent_base, modulus) * pow(target, exponent_target, modulus)) % modulus |
75 | 93 |
|
76 | | - # Skip if x is 0 or 1 (problematic starting points) |
77 | | - if x <= 1: |
| 94 | + # Skip if current_value is 0 or 1 (problematic starting points) |
| 95 | + if current_value <= 1: |
78 | 96 | continue |
79 | 97 |
|
80 | | - X, A, B = x, a, b # Tortoise and hare start at same position |
| 98 | + # Tortoise and hare start at same position |
| 99 | + tortoise_value, tortoise_exp_base, tortoise_exp_target = current_value, exponent_base, exponent_target |
| 100 | + hare_value, hare_exp_base, hare_exp_target = current_value, exponent_base, exponent_target |
81 | 101 |
|
82 | 102 | # Increased iteration limit for better coverage |
83 | | - max_iterations = max(int(math.sqrt(p)) * 2, p // 2) |
| 103 | + max_iterations = max(int(math.sqrt(modulus)) * 2, modulus // 2) |
84 | 104 | for i in range(1, max_iterations): |
85 | 105 | # Tortoise: one step |
86 | | - x, a, b = f(x, a, b) |
| 106 | + tortoise_value, tortoise_exp_base, tortoise_exp_target = pseudo_random_function( |
| 107 | + tortoise_value, tortoise_exp_base, tortoise_exp_target |
| 108 | + ) |
87 | 109 | # Hare: two steps |
88 | | - X, A, B = f(*f(X, A, B)) |
| 110 | + hare_value, hare_exp_base, hare_exp_target = pseudo_random_function( |
| 111 | + *pseudo_random_function(hare_value, hare_exp_base, hare_exp_target) |
| 112 | + ) |
89 | 113 |
|
90 | | - if x == X and i > 1: # Avoid immediate collision |
| 114 | + if tortoise_value == hare_value and i > 1: # Avoid immediate collision |
91 | 115 | # Collision found |
92 | | - r = (a - A) % (p - 1) |
93 | | - s = (B - b) % (p - 1) |
| 116 | + exponent_difference = (tortoise_exp_base - hare_exp_base) % (modulus - 1) |
| 117 | + target_difference = (hare_exp_target - tortoise_exp_target) % (modulus - 1) |
94 | 118 |
|
95 | | - if s == 0: |
| 119 | + if target_difference == 0: |
96 | 120 | break # Try with different starting point |
97 | 121 |
|
98 | 122 | try: |
99 | 123 | # Compute modular inverse using extended Euclidean algorithm |
100 | | - inv_s = pow(s, -1, p - 1) |
| 124 | + inverse_target_diff = pow(target_difference, -1, modulus - 1) |
101 | 125 | except ValueError: |
102 | 126 | break # No inverse, try different starting point |
103 | 127 |
|
104 | | - x_log = (r * inv_s) % (p - 1) |
| 128 | + discrete_log = (exponent_difference * inverse_target_diff) % (modulus - 1) |
105 | 129 |
|
106 | 130 | # Verify the solution |
107 | | - if pow(g, x_log, p) == h: |
108 | | - return x_log |
| 131 | + if pow(base, discrete_log, modulus) == target: |
| 132 | + return discrete_log |
109 | 133 | break # This attempt failed, try with different starting point |
110 | 134 |
|
111 | 135 | return None |
|
0 commit comments