proveprime.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #!/usr/bin/env python3
  2. import argparse
  3. import functools
  4. import math
  5. import os
  6. import re
  7. import subprocess
  8. import sys
  9. import itertools
  10. def gen_names():
  11. for i in itertools.count():
  12. name = "p{:d}".format(i)
  13. if name not in nameset:
  14. yield name
  15. nameset=set()
  16. names = gen_names()
  17. class YafuError(Exception):
  18. pass
  19. verbose = False
  20. def diag(*args):
  21. if verbose:
  22. print(*args, file=sys.stderr)
  23. factorcache = set()
  24. factorcachefile = None
  25. def cache_factor(f):
  26. if f not in factorcache:
  27. factorcache.add(f)
  28. if factorcachefile is not None:
  29. factorcachefile.write("{:d}\n".format(f))
  30. factorcachefile.flush()
  31. yafu = None
  32. yafu_pattern = re.compile(rb"^P\d+ = (\d+)$")
  33. def call_yafu(n):
  34. n_orig = n
  35. diag("starting yafu", n_orig)
  36. p = subprocess.Popen([yafu, "-v", "-v"], stdin=subprocess.PIPE,
  37. stdout=subprocess.PIPE)
  38. p.stdin.write("{:d}\n".format(n).encode("ASCII"))
  39. p.stdin.close()
  40. factors = []
  41. for line in iter(p.stdout.readline, b''):
  42. line = line.rstrip(b"\r\n")
  43. diag("yafu output:", line.decode())
  44. m = yafu_pattern.match(line)
  45. if m is not None:
  46. f = int(m.group(1))
  47. if n % f != 0:
  48. raise YafuError("bad yafu factor {:d}".format(f))
  49. factors.append(f)
  50. if f >> 64:
  51. cache_factor(f)
  52. n //= f
  53. p.wait()
  54. diag("done yafu", n_orig)
  55. return factors, n
  56. def factorise(n):
  57. allfactors = []
  58. for f in factorcache:
  59. if n % f == 0:
  60. n //= f
  61. allfactors.append(f)
  62. while n > 1:
  63. factors, n = call_yafu(n)
  64. allfactors.extend(factors)
  65. return sorted(allfactors)
  66. def product(ns):
  67. return functools.reduce(lambda a,b: a*b, ns, 1)
  68. smallprimes = set()
  69. commands = {}
  70. def proveprime(p, name=None):
  71. if p >> 32 == 0:
  72. smallprimes.add(p)
  73. return "{:d}".format(p)
  74. if name is None:
  75. name = next(names)
  76. print("{} = {:d}".format(name, p))
  77. fs = factorise(p-1)
  78. fs.reverse()
  79. prod = product(fs)
  80. qs = []
  81. for q in fs:
  82. newprod = prod // q
  83. if newprod * newprod * newprod > p:
  84. prod = newprod
  85. else:
  86. qs.append(q)
  87. assert prod == product(qs)
  88. assert prod * prod * prod > p
  89. qset = set(qs)
  90. qnamedict = {q: proveprime(q) for q in qset}
  91. qnames = [qnamedict[q] for q in qs]
  92. for w in itertools.count(2):
  93. assert pow(w, p-1, p) == 1, "{}={:d} is not prime!".format(name, p)
  94. diag("trying witness", w, "for", p)
  95. for q in qset:
  96. wpower = pow(w, (p-1) // q, p) - 1
  97. if math.gcd(wpower, p) != 1:
  98. break
  99. else:
  100. diag("found witness", w, "for", p)
  101. break
  102. commands[p]= (name, w, qnames)
  103. return name
  104. def main():
  105. parser = argparse.ArgumentParser(description='')
  106. parser.add_argument("prime", nargs="+",
  107. help="Number to prove prime. Can be prefixed by a "
  108. "variable name and '=', e.g. 'x=9999999967'.")
  109. parser.add_argument("--cryptsuite", action="store_true",
  110. help="Generate abbreviated Pockle calls suitable "
  111. "for the tests in cryptsuite.py.")
  112. parser.add_argument("--yafu", default="yafu",
  113. help="yafu binary to help with factoring.")
  114. parser.add_argument("-v", "--verbose", action="store_true",
  115. help="Write diagnostics to standard error.")
  116. parser.add_argument("--cache", help="Cache of useful factors of things.")
  117. args = parser.parse_args()
  118. global verbose, yafu
  119. verbose = args.verbose
  120. yafu = args.yafu
  121. if args.cache is not None:
  122. with open(args.cache, "r") as fh:
  123. for line in iter(fh.readline, ""):
  124. factorcache.add(int(line.rstrip("\r\n")))
  125. global factorcachefile
  126. factorcachefile = open(args.cache, "a")
  127. for ps in args.prime:
  128. name, value = (ps.split("=", 1) if "=" in ps
  129. else (None, ps))
  130. proveprime(int(value, 0), name)
  131. print("po = pockle_new()")
  132. if len(smallprimes) > 0:
  133. if args.cryptsuite:
  134. print("add_small(po, {})".format(
  135. ", ".join("{:d}".format(q) for q in sorted(smallprimes))))
  136. else:
  137. for q in sorted(smallprimes):
  138. print("pockle_add_small_prime(po, {:d})".format(q))
  139. for p, (name, w, qnames) in sorted(commands.items()):
  140. print("{cmd}(po, {name}, [{qs}], {w:d})".format(
  141. cmd = "add" if args.cryptsuite else "pockle_add_prime",
  142. name=name, w=w, qs=", ".join(qnames)))
  143. if __name__ == '__main__':
  144. main()