1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/contrib/proveprime.py

163 lines
4.7 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import argparse
import functools
import math
import os
import re
import subprocess
import sys
import itertools
def gen_names():
for i in itertools.count():
name = "p{:d}".format(i)
if name not in nameset:
yield name
nameset=set()
names = gen_names()
class YafuError(Exception):
pass
verbose = False
def diag(*args):
if verbose:
print(*args, file=sys.stderr)
factorcache = set()
factorcachefile = None
def cache_factor(f):
if f not in factorcache:
factorcache.add(f)
if factorcachefile is not None:
factorcachefile.write("{:d}\n".format(f))
factorcachefile.flush()
yafu = None
yafu_pattern = re.compile(rb"^P\d+ = (\d+)$")
def call_yafu(n):
n_orig = n
diag("starting yafu", n_orig)
p = subprocess.Popen([yafu, "-v", "-v"], stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
p.stdin.write("{:d}\n".format(n).encode("ASCII"))
p.stdin.close()
factors = []
for line in iter(p.stdout.readline, b''):
line = line.rstrip(b"\r\n")
diag("yafu output:", line.decode())
m = yafu_pattern.match(line)
if m is not None:
f = int(m.group(1))
if n % f != 0:
raise YafuError("bad yafu factor {:d}".format(f))
factors.append(f)
if f >> 64:
cache_factor(f)
n //= f
p.wait()
diag("done yafu", n_orig)
return factors, n
def factorise(n):
allfactors = []
for f in factorcache:
if n % f == 0:
n //= f
allfactors.append(f)
while n > 1:
factors, n = call_yafu(n)
allfactors.extend(factors)
return sorted(allfactors)
def product(ns):
return functools.reduce(lambda a,b: a*b, ns, 1)
smallprimes = set()
commands = {}
def proveprime(p, name=None):
if p >> 32 == 0:
smallprimes.add(p)
return "{:d}".format(p)
if name is None:
name = next(names)
print("{} = {:d}".format(name, p))
fs = factorise(p-1)
fs.reverse()
prod = product(fs)
qs = []
for q in fs:
newprod = prod // q
if newprod * newprod * newprod > p:
prod = newprod
else:
qs.append(q)
assert prod == product(qs)
assert prod * prod * prod > p
qset = set(qs)
qnamedict = {q: proveprime(q) for q in qset}
qnames = [qnamedict[q] for q in qs]
for w in itertools.count(2):
assert pow(w, p-1, p) == 1, "{}={:d} is not prime!".format(name, p)
diag("trying witness", w, "for", p)
for q in qset:
wpower = pow(w, (p-1) // q, p) - 1
if math.gcd(wpower, p) != 1:
break
else:
diag("found witness", w, "for", p)
break
commands[p]= (name, w, qnames)
return name
def main():
parser = argparse.ArgumentParser(description='')
parser.add_argument("prime", nargs="+",
help="Number to prove prime. Can be prefixed by a "
"variable name and '=', e.g. 'x=9999999967'.")
parser.add_argument("--cryptsuite", action="store_true",
help="Generate abbreviated Pockle calls suitable "
"for the tests in cryptsuite.py.")
parser.add_argument("--yafu", default="yafu",
help="yafu binary to help with factoring.")
parser.add_argument("-v", "--verbose", action="store_true",
help="Write diagnostics to standard error.")
parser.add_argument("--cache", help="Cache of useful factors of things.")
args = parser.parse_args()
global verbose, yafu
verbose = args.verbose
yafu = args.yafu
if args.cache is not None:
with open(args.cache, "r") as fh:
for line in iter(fh.readline, ""):
factorcache.add(int(line.rstrip("\r\n")))
global factorcachefile
factorcachefile = open(args.cache, "a")
for ps in args.prime:
name, value = (ps.split("=", 1) if "=" in ps
else (None, ps))
proveprime(int(value, 0), name)
print("po = pockle_new()")
if len(smallprimes) > 0:
if args.cryptsuite:
print("add_small(po, {})".format(
", ".join("{:d}".format(q) for q in sorted(smallprimes))))
else:
for q in sorted(smallprimes):
print("pockle_add_small_prime(po, {:d})".format(q))
for p, (name, w, qnames) in sorted(commands.items()):
print("{cmd}(po, {name}, [{qs}], {w:d})".format(
cmd = "add" if args.cryptsuite else "pockle_add_prime",
name=name, w=w, qs=", ".join(qnames)))
if __name__ == '__main__':
main()