Exemplo n.º 1
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)
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)