def main(): # Test local versions of libraries utils.test_python_version() utils.test_gmpy2_version() # Parse command line arguments parser = argparse.ArgumentParser(description="Generate a prime field, suited for being the underlying field of a twist-secure Edwards curve.") parser.add_argument("input_file", help="JSON file containing the BBS parameters (typically, the output of 02_generate_bbs_parameters.py).") parser.add_argument("output_file", help="Output file where this script will write the prime of the field and the current BBS parameters.") parser.add_argument("prime_size", type=int, help="Size of the prime (e.g. 256 bits)") args = parser.parse_args() # Check arguments output_file = args.output_file if os.path.exists(output_file): utils.exit_error("The output file '%s' already exists. Exiting."%(output_file)) size = int(args.prime_size) input_file = args.input_file with open(input_file, "r") as f: data = json.load(f) bbs_p = int(data["bbs_p"]) bbs_q = int(data["bbs_q"]) bbs_n = bbs_p * bbs_q bbs_s = int(data["bbs_s"]) % bbs_n # Check inputs print("Checking inputs...") if not subroutines.is_strong_strong_prime(bbs_p): utils.exit_error("bbs_p is not a strong strong prime.") if not subroutines.is_strong_strong_prime(bbs_q): utils.exit_error("bbs_q is not a strong strong prime.") # Initialize BBS bbs = bbsengine.BBS(bbs_p, bbs_q, bbs_s) # generate a "size"-bit prime "p" candidate_nbr = 0 print("Generating a prime field Fp (where p is congruent to 3 mod 4)...") while True: candidate_nbr += 1 bits = [1] + bbs.genbits(size-3) + [1,1] assert(len(bits) == size) p = 0 for bit in bits: p = (p << 1) | bit assert(p % 4 == 3) assert(gmpy2.bit_length(p) == size) if subroutines.deterministic_is_pseudo_prime(p): break utils.colprint("%d-bit prime found:"%size, str(p)) utils.colprint("The good candidate was number: ", str(candidate_nbr)) # Save p and the current bbs parameters to the output_file print("Saving p and the BBS parameters to %s"%(output_file)) bbs_s = bbs.s with open(output_file, "w") as f: json.dump({"p": int(p), "bbs_p": int(bbs_p), "bbs_q": int(bbs_q), "bbs_s": int(bbs_s)}, f, sort_keys=True)
def main(): # Test local versions of libraries utils.test_python_version() utils.test_gmpy2_version() utils.test_pari_version() utils.test_pari_seadata() now = datetime.now() # Parse command line arguments parser = argparse.ArgumentParser(description="Generate an Edwards curve over a given prime field, suited for cryptographic purposes.") parser.add_argument("input_file", help="""JSON file containing the BBS parameters and the prime of the underlying field (typically, the output of 03_generate_prime_field_using_bbs.py. """) parser.add_argument("output_file", help="Output file where this script will write the parameter d of the curve and the current BBS parameters.") parser.add_argument("--start", type=int, help="Number of the candidate to start with (default is 1).", default=1) parser.add_argument("--max_nbr_of_tests", type=int, help="Number of candidates to test before stopping the script (default is to continue until success).") parser.add_argument("--fast", help=""" While computing a the curve cardinality with SAE, early exit when the cardinality will obviously be divisible by a small integer > 4. This reduces the time required to find the final curve, but the cardinalities of previous candidates are not fully computed. """, default=False, action="store_true") args = parser.parse_args() # Check arguments print("Checking inputs...") output_file = args.output_file if os.path.exists(output_file): utils.exit_error("The output file '%s' already exists. Exiting."%(output_file)) input_file = args.input_file with open(input_file, "r") as f: data = json.load(f) # Declare a few important variables bbs_p = int(data["bbs_p"]) bbs_q = int(data["bbs_q"]) bbs_n = bbs_p * bbs_q bbs_s = int(data["bbs_s"]) % bbs_n p = int(data["p"]) start = max(int(args.start),1) max_nbr_of_tests = None if args.max_nbr_of_tests: max_nbr_of_tests = int(args.max_nbr_of_tests) if not subroutines.is_strong_strong_prime(bbs_p): utils.exit_error("bbs_p is not a strong strong prime.") if not subroutines.is_strong_strong_prime(bbs_q): utils.exit_error("bbs_q is not a strong strong prime.") if not (subroutines.deterministic_is_pseudo_prime(p) and p%4 == 3): utils.exit_error("p is not a prime congruent to 3 modulo 4.") # Initialize BBS print("Initializing BBS...") bbs = bbsengine.BBS(bbs_p, bbs_q, bbs_s) # Info about the prime field utils.colprint("Prime of the underlying prime field:", "%d (size: %d)"%(p, gmpy2.bit_length(p))) size = gmpy2.bit_length(p) # total number of bits queried to bbs for each test # Skip the first "start" candidates candidate_nbr = start-1 bbs.skipbits(size * (start-1)) # Start looking for "d" while True: if max_nbr_of_tests and candidate_nbr >= start + max_nbr_of_tests - 1: print("Did not find an adequate parameter, starting at candidate %d (included), limiting to %d candidates."%(start, max_nbr_of_tests)) utils.exit_error("Last candidate checked was number %d."%(candidate_nbr)) candidate_nbr += 1 bits = bbs.genbits(size) d = 0 for bit in bits: d = (d << 1) | bit print("The candidate number %d is d = %d (ellapsed time: %s)"%(candidate_nbr, d, str(datetime.now()-now))) # Test 1 if not utils.check(d != 0 and d < p, "d != 0 and d < p", 1): continue # Test 2 if not utils.check(gmpy2.legendre(d, p) == -1, "d is not a square modulo p", 2): continue # Test 3 if args.fast: cardinality = subroutines.sea_edwards(1, d, p, 4) else: cardinality = subroutines.sea_edwards(1, d, p) assert(cardinality % 4 == 0) q = cardinality>>2 if not utils.check(subroutines.deterministic_is_pseudo_prime(q), "The curve cardinality / 4 is prime", 3): continue # Test 4 trace = p+1-cardinality cardinality_twist = p+1+trace assert(cardinality_twist % 4 == 0) q_twist = cardinality_twist>>2 if not utils.check(subroutines.deterministic_is_pseudo_prime(q_twist), "The twist cardinality / 4 is prime", 4): continue # Test 5 if not utils.check(q != p and q_twist != p, "Curve and twist are safe against additive transfer", 5): continue # Test 6 embedding_degree = subroutines.embedding_degree(p, q) if not utils.check(embedding_degree > (q-1) // 100, "Curve is safe against multiplicative transfer", 6): continue # Test 7 embedding_degree_twist = subroutines.embedding_degree(p, q_twist) if not utils.check(embedding_degree_twist > (q_twist-1) // 100, "Twist is safe against multiplicative transfer", 7): continue # Test 8 D = subroutines.cm_field_discriminant(p, trace) if not utils.check(abs(D) >= 2**100, "Absolute value of the discriminant is larger than 2^100", 8): continue break # Find a base point while True: bits = bbs.genbits(size) y = 0 for bit in bits: y = (y<<1) | bit u = int((1 - y**2) * gmpy2.invert(1 - d*y**2, p)) % p if gmpy2.legendre(u, p) == -1: continue x = gmpy2.powmod(u, (p+1) // 4, p) (x,y) = subroutines.add_on_edwards(x, y, x, y, d, p) (x,y) = subroutines.add_on_edwards(x, y, x, y, d, p) if (x, y) == (0, 1): continue assert((x**2 + y**2) % p == (1 + d*x**2*y**2) % p) break # Print some informations utils.colprint("Number of the successful candidate:", str(candidate_nbr)) utils.colprint("Edwards elliptic curve parameter d is:", str(d)) utils.colprint("Number of points:", str(cardinality)) utils.colprint("Number of points on the twist:", str(cardinality_twist)) utils.colprint("Embedding degree of the curve:", "%d"%embedding_degree) utils.colprint("Embedding degree of the twist:", "%d"%embedding_degree_twist) utils.colprint("Discriminant:", "%d"%D) utils.colprint("Trace:", "%d"%trace) utils.colprint("Base point coordinates:", "(%d, %d)"%(x, y)) # Save p, d, x, y, etc. to the output_file print("Saving the parameters to %s"%output_file) bbs_s = bbs.s with open(output_file, "w") as f: json.dump({"p": int(p), "bbs_p": int(bbs_p), "bbs_q": int(bbs_q), "bbs_s": int(bbs_s), "candidate_nbr": int(candidate_nbr), "d": int(d), "cardinality": cardinality, "cardinality_twist": cardinality_twist, "embedding_degree": embedding_degree, "embedding_degree_twist": embedding_degree_twist, "discriminant": D, "trace": trace, "base_point_x": x, "base_point_y": y}, f, sort_keys=True)
def main(): # Test local versions of libraries utils.test_python_version() utils.test_gmpy2_version() # Parse command line arguments parser = argparse.ArgumentParser(description="Generate BBS parameters.") parser.add_argument( "input_file", help= """JSON file containing the seed used for generating the pseudo strong strong prime (the name is "seed"). The required quantity of entropy it should contain depends on bitsize. As a rule of thumb the seed should contain at least 4*bitsize bits of entropy.""" ) parser.add_argument( "output_file", help= """Output JSON file where this script will write the two generated strong strong primes "p" and "q". The output file should not exist already.""" ) parser.add_argument( "min_prime_bitsize", type=int, help="minimum strong strong prime bit size (e.g. 2048).") args = parser.parse_args() # Check arguments output_file = args.output_file if os.path.exists(output_file): utils.exit_error("The output file '%s' already exists. Exiting." % (output_file)) # Declare a few important variables min_prime_bitsize = args.min_prime_bitsize input_file = args.input_file with open(input_file, "r") as f: data = json.load(f) seed = int(data["seed"]) seed_upper_bound = int(data["seed_upper_bound"]) approx_seed_entropy = math.floor(gmpy2.log2(seed_upper_bound)) utils.colprint("Minimum strong strong prime size:", str(min_prime_bitsize)) utils.colprint("Approximate seed entropy:", str(approx_seed_entropy)) # Precomputations first_primes = [2] # List of the first primes PI = 2 # Product of the primes in "first_primes" strong_strong_integers = [ [1] ] # strong_strong_integers[i] is the list of all strong strong integers modulo # first_primes[i] number_of_strong_strong_integers = [ 1 ] # number_of_strong_strong_integers[i] is the number of elements of the list # strong_strong_integers[i] C = 1 # Product of the elements of "number_of_strong_strong_integers" while not 2**(min_prime_bitsize - 2) < PI: p = int(gmpy2.next_prime(first_primes[-1])) first_primes.append(p) PI *= p ssi = [c for c in range(p) if is_strong_strong_basis(c, p)] strong_strong_integers.append(ssi) number_of_strong_strong_integers.append(len(ssi)) C *= len(ssi) utils.colprint("Number of primes considered:", str(len(first_primes))) utils.colprint("Number of strong strong integers to choose from:", "about 2^%f" % (gmpy2.log2(C))) # Check that the seed is long enough if seed_upper_bound < C**2 * (1 << (2 * min_prime_bitsize)): utils.exit_error("The seed does not contain the required entropy.") # Precomputations for the CRT mu = [gmpy2.divexact(PI, p) for p in first_primes] delta = [gmpy2.invert(x, y) for x, y in zip(mu, first_primes)] gamma = [gmpy2.mul(x, y) for x, y in zip(mu, delta)] # Generate the first strong prime print("Generating the first strong strong prime...") (p, seed) = generate_strong_strong_prime(seed, min_prime_bitsize, strong_strong_integers, number_of_strong_strong_integers, gamma, PI) utils.colprint("\tThis is the first strong strong prime:", str(p)) # Generate the second strong prime print("Generating the second strong strong prime...") (q, seed) = generate_strong_strong_prime(seed, min_prime_bitsize, strong_strong_integers, number_of_strong_strong_integers, gamma, PI) utils.colprint("\tThis is the second strong strong prime:", str(q)) # Generate the BBS start print("Generating the BBS starting point...") n = p * q s = seed % n while s == 0 or s == 1 or s == p or s == q: s = (s + 1) % n s0 = (s**2) % n utils.colprint("\tThis is the starting point s0 of BBS:", str(s0)) # Save p,q, and s to the output_file print("Saving p,q, and s0 to %s" % (output_file)) with open(output_file, "w") as f: json.dump({ "bbs_p": int(p), "bbs_q": int(q), "bbs_s": int(s0) }, f, sort_keys=True)
def main(): # Test local versions of libraries utils.test_python_version() utils.test_gmpy2_version() # Parse command line arguments parser = argparse.ArgumentParser(description="Generate BBS parameters.") parser.add_argument("input_file", help="""JSON file containing the seed used for generating the pseudo strong strong prime (the name is "seed"). The required quantity of entropy it should contain depends on bitsize. As a rule of thumb the seed should contain at least 4*bitsize bits of entropy.""") parser.add_argument("output_file", help="""Output JSON file where this script will write the two generated strong strong primes "p" and "q". The output file should not exist already.""") parser.add_argument("min_prime_bitsize", type=int, help="minimum strong strong prime bit size (e.g. 2048).") args = parser.parse_args() # Check arguments output_file = args.output_file if os.path.exists(output_file): utils.exit_error("The output file '%s' already exists. Exiting."%(output_file)) # Declare a few important variables min_prime_bitsize = args.min_prime_bitsize input_file = args.input_file with open(input_file, "r") as f: data = json.load(f) seed = int(data["seed"]) seed_upper_bound = int(data["seed_upper_bound"]) approx_seed_entropy = math.floor(gmpy2.log2(seed_upper_bound)) utils.colprint("Minimum strong strong prime size:", str(min_prime_bitsize)) utils.colprint("Approximate seed entropy:", str(approx_seed_entropy)) # Precomputations first_primes = [2] # List of the first primes PI = 2 # Product of the primes in "first_primes" strong_strong_integers = [[1]] # strong_strong_integers[i] is the list of all strong strong integers modulo # first_primes[i] number_of_strong_strong_integers = [1] # number_of_strong_strong_integers[i] is the number of elements of the list # strong_strong_integers[i] C = 1 # Product of the elements of "number_of_strong_strong_integers" while not 2**(min_prime_bitsize-2) < PI: p = int(gmpy2.next_prime(first_primes[-1])) first_primes.append(p) PI *= p ssi = [c for c in range(p) if is_strong_strong_basis(c, p)] strong_strong_integers.append(ssi) number_of_strong_strong_integers.append(len(ssi)) C *= len(ssi) utils.colprint("Number of primes considered:", str(len(first_primes))) utils.colprint("Number of strong strong integers to choose from:", "about 2^%f"%(gmpy2.log2(C))) # Check that the seed is long enough if seed_upper_bound < C**2 * (1 << (2 * min_prime_bitsize)): utils.exit_error("The seed does not contain the required entropy.") # Precomputations for the CRT mu = [gmpy2.divexact(PI,p) for p in first_primes] delta = [gmpy2.invert(x,y) for x,y in zip(mu,first_primes)] gamma = [gmpy2.mul(x,y) for x,y in zip(mu,delta)] # Generate the first strong prime print("Generating the first strong strong prime...") (p,seed) = generate_strong_strong_prime(seed, min_prime_bitsize, strong_strong_integers, number_of_strong_strong_integers, gamma, PI) utils.colprint("\tThis is the first strong strong prime:", str(p)) # Generate the second strong prime print("Generating the second strong strong prime...") (q,seed) = generate_strong_strong_prime(seed, min_prime_bitsize, strong_strong_integers, number_of_strong_strong_integers, gamma, PI) utils.colprint("\tThis is the second strong strong prime:", str(q)) # Generate the BBS start print("Generating the BBS starting point...") n = p*q s = seed % n while s == 0 or s == 1 or s == p or s == q: s = (s+1) % n s0 = (s**2) % n utils.colprint("\tThis is the starting point s0 of BBS:", str(s0)) # Save p,q, and s to the output_file print("Saving p,q, and s0 to %s"%(output_file)) with open(output_file, "w") as f: json.dump({"bbs_p": int(p), "bbs_q": int(q), "bbs_s": int(s0)}, f, sort_keys=True)