import hashlib # for a PRNG, SHA-1 is standard and sufficiently secure def hash(seed): h = hashlib.sha1(); h.update(seed); return h.digest() seedbytes = 20 # 160-bit size for seed, determined by SHA-1 output size # 224-bit prime p produced by very similar procedure, shown in separate file p = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF k = GF(p); R. = k[] def secure(A,B): n = EllipticCurve([k(A),k(B)]).cardinality() return (n < p and n.is_prime() and Integers(n)(p).multiplicative_order() * 100 >= n-1) def int2str(seed,bytes): # standard big-endian encoding of integer seed return ''.join([chr((seed//256^i)%256) for i in reversed(range(bytes))]) def str2int(seed): return Integer(seed.encode('hex'),16) def update(seed): # add 1 to seed, viewed as integer return int2str(str2int(seed) + 1,len(seed)) def fullhash(seed): return str2int(hash(seed) + hash(update(seed))) % 2^223 def real2str(seed,bytes): # most significant bits of real number between 0 and 1 return int2str(Integer(floor(RealField(8*bytes+8)(seed)*256^bytes)),bytes) nums = real2str(exp(1)/16,7*seedbytes) # enough bits for all curve sizes S = nums[2*seedbytes:3*seedbytes] # previous bytes are used for 160 and 192 while True: A = fullhash(S) if not (k(A)*x^4+3).roots(): S = update(S); continue while True: S = update(S) B = fullhash(S) if not k(B).is_square(): break if not secure(A,B): S = update(S); continue print 'p',hex(p).upper() print 'A',hex(A).upper() print 'B',hex(B).upper() break # output: # p D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF # A 68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 # B 2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B