Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/attacks/pseudoprimes/miller_rabin.py
2589 views
1
import logging
2
import os
3
import sys
4
from math import gcd
5
6
from sage.all import is_prime
7
from sage.all import kronecker
8
from sage.all import next_prime
9
10
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))))
11
if sys.path[1] != path:
12
sys.path.insert(1, path)
13
14
from shared.crt import fast_crt
15
16
17
def _generate_s(A, k):
18
S = []
19
for a in A:
20
# Possible non-residues mod 4a of potential primes p
21
Sa = set()
22
for p in range(1, 4 * a, 2):
23
if kronecker(a, p) == -1:
24
Sa.add(p)
25
26
# Subsets of Sa that meet the intersection requirement
27
Sk = []
28
for ki in k:
29
assert gcd(ki, 4 * a) == 1
30
Sk.append({pow(ki, -1, 4 * a) * (s + ki - 1) % (4 * a) for s in Sa})
31
32
S.append(Sa.intersection(*Sk))
33
34
return S
35
36
37
# Brute forces a combination of residues from S by backtracking
38
# X already contains the remainders mod each k
39
# M already contains each k
40
def _backtrack(S, A, X, M, i):
41
if i == len(S):
42
return fast_crt(X, M)
43
44
M.append(4 * A[i])
45
for za in S[i]:
46
X.append(za)
47
try:
48
fast_crt(X, M)
49
z, m = _backtrack(S, A, X, M, i + 1)
50
if z is not None and m is not None:
51
return z, m
52
except ValueError:
53
pass
54
X.pop()
55
56
M.pop()
57
return None, None
58
59
60
def generate_pseudoprime(A, k2=None, k3=None, min_bit_length=0):
61
"""
62
Generates a pseudoprime of the form p1 * p2 * p3 which passes the Miller-Rabin primality test for the provided bases.
63
More information: R. Albrecht M. et al., "Prime and Prejudice: Primality Testing Under Adversarial Conditions"
64
:param A: the bases
65
:param k2: the k2 value (default: next_prime(A[-1]))
66
:param k3: the k3 value (default: next_prime(k2))
67
:param min_bit_length: the minimum bit length of the generated pseudoprime (default: 0)
68
:return: a tuple containing the pseudoprime n, as well as its 3 prime factors
69
"""
70
A.sort()
71
if k2 is None:
72
k2 = int(next_prime(A[-1]))
73
if k3 is None:
74
k3 = int(next_prime(k2))
75
while True:
76
logging.info(f"Trying {k2 = } and {k3 = }...")
77
X = [pow(-k3, -1, k2), pow(-k2, -1, k3)]
78
M = [k2, k3]
79
S = _generate_s(A, M)
80
logging.info(f"{S = }")
81
z, m = _backtrack(S, A, X, M, 0)
82
if z and m:
83
logging.info(f"Found residue {z} and modulus {m}")
84
i = (2 ** (min_bit_length // 3)) // m
85
while True:
86
p1 = int(z + i * m)
87
p2 = k2 * (p1 - 1) + 1
88
p3 = k3 * (p1 - 1) + 1
89
if is_prime(p1) and is_prime(p2) and is_prime(p3):
90
return p1 * p2 * p3, p1, p2, p3
91
92
i += 1
93
else:
94
k3 = int(next_prime(k3))
95
96