Example #1
0
 def splitext(filename):
     # not using os.path.splitext as it would return .gz instead of
     # .tar.gz
     if filename.endswith('.tar.gz'):
         return '.tar.gz'
     elif filename.endswith('.exe'):
         return '.exe'
     else:
         utils.exit_error(
             'Unknown agent format for {0}. '
             'Must be either tar.gz or exe'.format(filename))
 def splitext(filename):
     # not using os.path.splitext as it would return .gz instead of
     # .tar.gz
     if filename.endswith('.tar.gz'):
         return '.tar.gz'
     elif filename.endswith('.exe'):
         return '.exe'
     else:
         utils.exit_error(
             'Unknown agent format for {0}. '
             'Must be either tar.gz or exe'.format(filename))
Example #3
0
 def parse_equation(self):
     neg = re.findall(r"[X|x]\^\-", self.__equation)
     fpow = re.findall(r"[X|x]\^(\d+\.\d+)", self.__equation)
     check_validity = self.__check_validity()
     if neg or fpow or check_validity:
         exit_error(
             f"{self.color.red}The polynomial equation is not valid!{self.color.none}"
         )
     self.core_equation = re.split(r"(\s)?=(\s)?", self.__equation)[0]
     self.start_egal = re.split(r"(\s)?=(\s)?", self.__equation)[3]
     if self.core_equation and self.start_egal:
         pows = self.__parse_select_pows()
         self.__reduct_equation(pows)
         self.__parse_get_degree()
     else:
         exit_error(
             f"{self.color.red}The polynomial equation is not valid!{self.color.none}"
         )
def main():

    # Test local versions of libraries

    utils.test_python_version()
    utils.test_gmpy2_version()

    # Parse command line arguments

    parser = argparse.ArgumentParser(
        description=
        "From a table of draws, output a seed of appropriate length.")

    parser.add_argument("input_draw_file", help="""Text file with draws.""")
    parser.add_argument(
        "output_seed_file",
        help=
        """JSON file where we can store the seed computed from the draws.""")
    parser.add_argument(
        "entropy_to_gather",
        help="""Minimum entropy to extract before drawing lone bits.""")
    parser.add_argument("--nbr_lone_bits",
                        type=int,
                        help="""Number of lone bits to extract.""",
                        default=0)

    args = parser.parse_args()

    # Check arguments

    output_seed_file = args.output_seed_file
    if os.path.exists(output_seed_file):
        utils.exit_error("The output file '%s' already exists. Exiting." %
                         (output_seed_file))

    # Declare a few important variables

    two_pow_entropy_to_gather = (1 << int(args.entropy_to_gather))

    seed = 0
    L = 1  # before lone bits are drawn, seed lies in [0,L - 1]

    lone_bits_part = 0
    nbr_lone_bits = args.nbr_lone_bits

    # Scan the input file, construct the seed

    with open(args.input_draw_file, "r") as f:

        for line in f:

            if not line or line.strip() == "" or line.startswith("#"):
                continue

            (draw_id, m, n, draw) = re.split("\s+", line.strip(), maxsplit=3)

            if draw == "None":
                continue

            m = int(m)
            n = int(n)
            draw = [int(x) for x in draw.split(",")]
            index = index_from_draw(draw, m)

            if L < two_pow_entropy_to_gather:

                print("Draw %s used to extract entropy" % (draw_id))
                seed = gmpy2.bincoef(n, m) * seed + index
                L *= gmpy2.bincoef(n, m)

            else:

                print("Draw %s used to extract a lone bit" % (draw_id))
                b = index & 1
                seed += L * (b << (args.nbr_lone_bits - nbr_lone_bits))
                nbr_lone_bits -= 1

            if L >= two_pow_entropy_to_gather and nbr_lone_bits == 0:
                break

    if nbr_lone_bits > 0 or L < two_pow_entropy_to_gather:
        utils.exit_error(
            "There wasn't enough draws to collect to request quantity of entropy and lone bits."
        )

    seed_upper_bound = L * 2**(args.nbr_lone_bits)
    seed_entropy = math.floor(gmpy2.log2(seed_upper_bound))
    print(
        "The seed contains more than %d bits of entropy (including the %s lone bits)."
        % (seed_entropy, args.nbr_lone_bits))
    print("The seed is %d" % (seed))

    print("Saving the seed to %s" % (output_seed_file))
    with open(output_seed_file, "w") as f:
        json.dump(
            {
                "seed": int(seed),
                "seed_upper_bound": int(seed_upper_bound),
                "approx_seed_entropy": int(seed_entropy),
                "lone_bits": int(args.nbr_lone_bits)
            },
            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="From a table of draws, output a seed of appropriate length.")

    parser.add_argument("input_draw_file", help="""Text file with draws.""")
    parser.add_argument("output_seed_file", help="""JSON file where we can store the seed computed from the draws.""")
    parser.add_argument("entropy_to_gather", help="""Minimum entropy to extract before drawing lone bits.""")
    parser.add_argument("--nbr_lone_bits", type=int, help="""Number of lone bits to extract.""", default=0)
    
    args = parser.parse_args()


    # Check arguments
    
    output_seed_file = args.output_seed_file
    if os.path.exists(output_seed_file):
        utils.exit_error("The output file '%s' already exists. Exiting."%(output_seed_file))


    # Declare a few important variables
    
    two_pow_entropy_to_gather = (1<<int(args.entropy_to_gather))
    
    seed = 0
    L = 1 # before lone bits are drawn, seed lies in [0,L - 1]

    lone_bits_part = 0
    nbr_lone_bits = args.nbr_lone_bits


    # Scan the input file, construct the seed
    
    with open(args.input_draw_file, "r") as f:
        
        for line in f:

            if not line or line.strip() == "" or line.startswith("#"):
                continue

            (draw_id, m, n, draw) = re.split("\s+", line.strip(), maxsplit=3)

            if draw == "None":
                continue
            
            m = int(m)
            n = int(n)
            draw = [ int(x) for x in draw.split(",") ]
            index = index_from_draw(draw,m)

            if L < two_pow_entropy_to_gather:
                
                print("Draw %s used to extract entropy"%(draw_id))
                seed = gmpy2.bincoef(n,m)*seed + index
                L *= gmpy2.bincoef(n,m)
                
            else:
                
                print("Draw %s used to extract a lone bit"%(draw_id))
                b = index & 1
                seed += L * (b << (args.nbr_lone_bits - nbr_lone_bits))
                nbr_lone_bits -= 1

            if L >= two_pow_entropy_to_gather and nbr_lone_bits == 0:
                break

    if nbr_lone_bits > 0 or L < two_pow_entropy_to_gather:
        utils.exit_error("There wasn't enough draws to collect to request quantity of entropy and lone bits.")

    seed_upper_bound = L * 2**(args.nbr_lone_bits)
    seed_entropy = math.floor(gmpy2.log2(seed_upper_bound))
    print("The seed contains more than %d bits of entropy (including the %s lone bits)."%(seed_entropy,args.nbr_lone_bits))
    print("The seed is %d"%(seed))

    print("Saving the seed to %s"%(output_seed_file))
    with open(output_seed_file, "w") as f:
        json.dump({"seed": int(seed),
                   "seed_upper_bound": int(seed_upper_bound),
                   "approx_seed_entropy": int(seed_entropy),
                   "lone_bits": int(args.nbr_lone_bits)}, 
                  f,
                  sort_keys=True)
Example #6
0
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)
Example #7
0
def main():

    now = datetime.now()
    
    # Parse command line arguments

    parser = argparse.ArgumentParser(description=
                                     """The script takes as an input a list of integers. If any of those integers fails 
                                     a pseudo primality test, this script exists immediately. Otherwise, it tries to 
                                     output a proof of primality for each of these pseudo primes.
                                     """)
    
    parser.add_argument("integers", type=int, nargs="+", help="List of all integers to consider.")

    args = parser.parse_args()


    # Check arguments
    
    for n in args.integers:
        if not subroutines.deterministic_is_pseudo_prime(n):
            utils.exit_error("%d is not prime."%(n))
        
    # Declare a few important variables. In particular, large_factors[p] will contain a list [[p1,m1],[p2,m2],...]  such
    # that p1^m1*p2^m2*... > sqrt(p), for all "p" in "pseudo_primes".

    pseudo_primes = set(args.integers)
    large_factors = {} 

    
    # Compute the dictionnary "large_factors"

    while True:
        
        if not pseudo_primes:
            break
        
        p = max(pseudo_primes)
        pseudo_primes.remove(p)

        if p == 2:
            continue
        
        print("Factoring %d - 1"%(p))
        
        all_factors = subroutines.factor(p-1)

        A = 1
        f = []
        while A <= math.sqrt(p):
            [q,m] = all_factors.pop()
            A *= q**m
            f += [[q,m]]
            pseudo_primes.add(q)
            
        large_factors[p] = list(reversed(f))

    # Prove primes
        
    proven_primes = {2: []} # For N > 2, proven_primes[N] will be an array [large_factors[N],a], where proof is a
                            # dictionnary s.t. len(a) == len(large_factors[N]) and a[p] is the a_p corresponding to the
                            # factor p = large_factors[N][p] in the Pocklington method.

    while True:

        if not large_factors:
            break

        N = min(large_factors.keys())
        # Generalized Pocklington method to show that N is prime
        f = large_factors.pop(N) # large factors of N - 1
        a = {}
        for p,m in f:
            assert((N-1) % p == 0)
            for a_p in range(2,N):
                if gmpy2.powmod(a_p, N-1, N) != 1:
                    continue
                if gmpy2.gcd(gmpy2.powmod(a_p, (N-1)//p, N) - 1, N) != 1:
                    continue
                break
            a[p] = a_p
        proven_primes[N] = [f,a]

        
    # Print proofs

    for N in sorted(proven_primes.keys()):
        if N == 2:
            continue
        print("Proof that N = %d is prime:"%(N))
        f = proven_primes[N][0]
        a = proven_primes[N][1]
        A = 1
        for p,m in proven_primes[N][0]:
            A *= p**m
        assert((N-1) % A == 0)
        B = (N-1) // A
        print("\tN - 1 = A * B with")
        print("\tA = %d = %s"%(A,factors_to_string(f)))
        print("\tB = %d"%(B))
        assert(gmpy2.gcd(A,B) == 1)
        assert(A > math.sqrt(N))
        print("\tA and B are relatively prime and A > sqrt(N).")
        print("\tPrime factor(s) of A: %s"%(", ".join([str(p) for p,m in f])))
        for p,m in f:
            assert(gmpy2.powmod(a[p], N-1, N) == 1)
            assert(gmpy2.gcd(gmpy2.powmod(a[p], (N-1) // p, N), N) == 1)
            print("\tFor p = %d, we have %d^(N-1) mod N = 1 and gcd(%d^((N-1)/p) - 1, N) = 1"%(p, a[p], a[p]))
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()
    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)