mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-08 08:58:00 +00:00
New script contrib/proveprime.py.
This generates primality certificates for numbers, in the form of Python / testcrypt code that calls Pockle methods. It factors p-1 by calling out to the 'yafu' utility, which is a moderately sophisticated integer factoring tool (including ECC and quadratic sieve methods) that runs as a standalone command-line program. Also added a Pockle test generated as output from this script, which verifies the primality of the three NIST curves' moduli and their generators' orders. I already had Pockle certificates for the moduli and orders used in EdDSA, so this completes the set, and it does it without me having had to do a lot of manual work.
This commit is contained in:
parent
9f4bd6c552
commit
47c2bc38d1
162
contrib/proveprime.py
Executable file
162
contrib/proveprime.py
Executable file
@ -0,0 +1,162 @@
|
||||
#!/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()
|
@ -1079,6 +1079,49 @@ class keygen(MyTestBase):
|
||||
add(po, p6, [p3,p4], 2)
|
||||
add(po, p, [p2,p5,p6], 2)
|
||||
|
||||
# Combined certificate for the moduli and generator orders of
|
||||
# the three NIST curves, generated by contrib/proveprime.py
|
||||
# (with some cosmetic tidying)
|
||||
p256 = 2**256 - 2**224 + 2**192 + 2**96 - 1
|
||||
p384 = 2**384 - 2**128 - 2**96 + 2**32 - 1
|
||||
p521 = 2**521 - 1
|
||||
order256 = p256 - 0x4319055358e8617b0c46353d039cdaae
|
||||
order384 = p384 - 0x389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c
|
||||
t = 0x5ae79787c40d069948033feb708f65a2fc44a36477663b851449048e16ec79bf6
|
||||
order521 = p521 - t
|
||||
p0 = order384 // 12895580879789762060783039592702
|
||||
p1 = 1059392654943455286185473617842338478315215895509773412096307
|
||||
p2 = 55942463741690639
|
||||
p3 = 37344768852931
|
||||
p4 = order521 // 1898873518475180724503002533770555108536
|
||||
p5 = p4 // 994165722
|
||||
p6 = 144471089338257942164514676806340723
|
||||
p7 = p384 // 2054993070433694
|
||||
p8 = 1357291859799823621
|
||||
po = pockle_new()
|
||||
add_small(po, 2, 3, 5, 11, 17, 19, 31, 41, 53, 67, 71, 109, 131, 149,
|
||||
157, 257, 521, 641, 1613, 2731, 3407, 6317, 8191, 8389,
|
||||
14461, 17449, 38189, 38557, 42641, 51481, 61681, 65537,
|
||||
133279, 248431, 312289, 409891, 490463, 858001, 6700417,
|
||||
187019741)
|
||||
add(po, p3, [149, 11, 5, 3, 2], 3)
|
||||
add(po, p2, [p3], 2)
|
||||
add(po, p8, [6317, 67, 2, 2], 2)
|
||||
add(po, p6, [133279, 14461, 109, 3], 7)
|
||||
add(po, p1, [p2, 248431], 2)
|
||||
add(po, order256, [187019741, 38189, 17449, 3407, 131, 71, 2, 2, 2, 2],
|
||||
7)
|
||||
add(po, p256, [6700417, 490463, 65537, 641, 257, 17, 5, 5, 3, 2], 6)
|
||||
add(po, p0, [p1], 2)
|
||||
add(po, p7, [p8, 312289, 38557, 8389, 11, 2], 3)
|
||||
add(po, p5, [p6, 19], 2)
|
||||
add(po, order384, [p0], 2)
|
||||
add(po, p384, [p7], 2)
|
||||
add(po, p4, [p5], 2)
|
||||
add(po, order521, [p4], 2)
|
||||
add(po, p521, [858001, 409891, 61681, 51481, 42641, 8191, 2731, 1613,
|
||||
521, 157, 131, 53, 41, 31, 17, 11, 5, 5, 3, 2], 3)
|
||||
|
||||
def testPockleNegative(self):
|
||||
def add_small(po, p):
|
||||
self.assertEqual(pockle_add_small_prime(po, p), 'POCKLE_OK')
|
||||
|
Loading…
Reference in New Issue
Block a user