diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8617026 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2020 Jan Jancar, Vladimir Sedlacek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index df9c0ff..095207f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # std-curves +See our [website](https://neuromancer.sk/std/). + **Standard curve database.** This repository contains a list of standardised elliptic curves, collected from many standards by the team at [Centre for Research on Cryptography and Security](https://crocs.fi.muni.cz). For our other projects related to elliptic curve cryptography, see: @@ -9,11 +11,21 @@ projects related to elliptic curve cryptography, see: reverse-engineering ECC implementations from devices - [ecgen](https://github.com/J08nY/ecgen): A tool for generating EC domain parameters -The curve listing includes its parameters, computed characteristics such as number of points or j-invariant. +The curve listing includes its parameters, computed characteristics such as number of points or j-invariant as +well as SAGE code which can be used to instantiate the curve and a JSON export of all of the curve data. New curves are currently being added, the database is definitely not complete. +The presence of a certain curve in this database does not mean that the curve is secure, only that it is notable +enough or that someone suggested its use in a publication or a standard. We made a best effort attempt to make +sure all parameters presented here are correct, however mistakes could have happened on data import and thus +double-checking with the source document is recommended. + ## Format The curves are stored in JSON files, grouped by category/source in directories. -See `schema.json` for the JSON schema of the files. See `analyze.sage` for a SAGE -script which parses the JSON and constructs a SAGE `EllipticCurve` object from it. \ No newline at end of file +See `schema.json` for the JSON schema of the files. + +## Website + +The website is hosted at , its sources +are available in the `page` branch. diff --git a/analyze.sage b/analyze.sage deleted file mode 100755 index 5f82677..0000000 --- a/analyze.sage +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env sage -import json -import sys -import click -from copy import copy -from cysignals.alarm import alarm, cancel_alarm, AlarmInterrupt - -config = None - -def embedding_degree_q(q, r): - '''returns embedding degree with respect to q''' - return Mod(q,r).multiplicative_order() - -def embedding_degree(E, r): - '''returns relative embedding degree with respect to E''' - q = (E.base_field()).order() - return embedding_degree_q(q, r) - -def ext_card(E, deg): - '''returns curve cardinality over deg-th relative extension''' - card_low = E.cardinality() - q = (E.base_field()).order() - tr = q + 1 - card_low - s_old, s_new = 2, tr - for i in [2..deg]: - s_old, s_new = s_new, tr * s_new - q * s_old - card_high = q^deg + 1 - s_new - return card_high - -def extend(E, deg): - '''returns curve over deg-th relative extension''' - q = E.base_field().order() - R. = E.base_field()[] - pol = R.irreducible_element(deg) - Fext = GF(q^deg, name = 'z', modulus = pol) - EE = E.base_extend(Fext) - return EE - -def find_least_torsion(E, r): - '''Find the minimal extension which contains Z_r''' - for deg in divisors(r^2-1): - if ext_card(E, deg) % r == 0: - return deg - return None - -def is_torsion_cyclic(E, r, deg): - card = ext_card(E, deg) - assert card % r^2 == 0 - m = ZZ(card / r) - EE = extend(E, deg) - for j in [1..5]: - P = EE.random_element() - if m*P != EE(0): - return True - return False - -def find_full_torsion(E, r, least): - '''Find the minimal extension which contains the full torsion''' - p = E.base_field().order() - q = p^least - k = embedding_degree_q(q, r) - if k > 1: - return k * least - else: - card = ext_card(E, least) - if (card % r^2) == 0 and not is_torsion_cyclic(E, r, least): - return least - else: - return r * least - -def find_torsions(E, r): - '''Find both minimal extensions which contain Z_r and then the full torsion''' - least = find_least_torsion(E, r) - if least == r^2-1: - full = least - else: - full = find_full_torsion(E, r, least) - return least, full - -def get_curve_group(data): - '''Construct an EllipticCurve and a generator from the parsed JSON.''' - if data["field"]["type"] == "Prime": - p = int(data["field"]["p"], 16) - K = GF(p) - if data["form"] == "Weierstrass": - a = K(int(data["params"]["a"], 16)) - b = K(int(data["params"]["b"], 16)) - E = EllipticCurve(K, (a, b)) - G = E(K(int(data["generator"]["x"], 16)), K(int(data["generator"]["y"], 16))) - elif data["form"] == "Edwards": - c = K(int(data["params"]["c"], 16)) - if c != 1: - raise NotImplementedError - d = K(int(data["params"]["d"], 16)) - E = EllipticCurve(K, (0, K(2 * (1 + d)/(1 - d)^2), 0, K(1/(1 - d)^2), 0)) - G = None - elif data["form"] == "Montgomery": - A = K(int(data["params"]["a"], 16)) - B = K(int(data["params"]["b"], 16)) - E = EllipticCurve(K, ((3 - A^2)/(3 * B^2), (2 * A^3 - 9 * A)/(27 * B^3))) - G = E(K(int(data["generator"]["x"], 16))/B + A/(3*B), K(int(data["generator"]["y"], 16))/B) - elif data["form"] == "TwistedEdwards": - a = K(int(data["params"]["a"], 16)) - d = K(int(data["params"]["d"], 16)) - E = EllipticCurve(K, (K(-1/48) * (a^2 + 14*a*d + d^2),K(1/864) * (a + d) * (-a^2 + 34*a*d - d^2))) - x = K(int(data["generator"]["x"], 16)) - y = K(int(data["generator"]["y"], 16)) - G = E((5*a + a*y - 5*d*y - d)/(12 - 12*y), (a + a*y - d*y -d)/(4*x - 4*x*y)) - E.set_order(int(data["order"], 16) * int(data["cofactor"], 16)) - return E, G - elif data["field"]["type"] == "Binary": - F. = GF(2)[] - poly = sum(F(int(elem["coeff"], 16)) * x^elem["power"] for elem in data["field"]["poly"]) - K = GF(2^data["field"]["degree"], name="x", modulus=poly) - E = EllipticCurve(K, (1, K.fetch_int(int(data["params"]["a"], 16)), 0, 0, K.fetch_int(int(data["params"]["b"], 16)))) - E.set_order(int(data["order"], 16) * int(data["cofactor"], 16)) - G = E(K.fetch_int(int(data["generator"]["x"], 16)), K.fetch_int(int(data["generator"]["y"], 16))) - return E, G - elif data["field"]["type"] == "Extension": - base = int(data["field"]["base"], 16) - F. = GF(base)[] - poly = sum(F(int(elem["coeff"], 16)) * x^elem["power"] for elem in data["field"]["poly"]) - K = GF(base^data["field"]["degree"], name="x", modulus=poly) - a = K(sum(F(int(elem["coeff"], 16)) * x^elem["power"] for elem in data["params"]["a"])) - b = K(sum(F(int(elem["coeff"], 16)) * x^elem["power"] for elem in data["params"]["b"])) - E = EllipticCurve(K, (a, b)) - E.set_order(int(data["order"], 16) * int(data["cofactor"], 16)) - gx = K(sum(F(int(elem["coeff"], 16)) * x^elem["power"] for elem in data["generator"]["x"])) - gy = K(sum(F(int(elem["coeff"], 16)) * x^elem["power"] for elem in data["generator"]["y"])) - G = E(gx, gy) - return E, G - else: - raise ValueError() - -def info(msg, level, **kwargs): - if config["verbose"] >= level: - click.secho(msg, **kwargs) - -def to_int(felement): - if felement.base_ring().characteristic() == 2: - c = reversed(felement.polynomial().coefficients(sparse=False)) - s = "".join(map(str, c)) - return int(s, 2) - else: - return int(felement) - -def analyze(data): - if config["curves"] and data["name"] not in config["curves"]: - return data - info(f"[{data['name']}] * Start * ", 0, fg="green", bold=True, err=True) - try: - curve, generator = get_curve_group(data) - except ValueError as err: - click.secho("Invalid input!", fg="red", err=True) - click.secho(str(err), fg="red", err=True) - return data - except NotImplementedError: - click.secho("Support for Edwards curves not yet implemented.", fg="red", err=True) - return data - if "characteristics" not in data: - data["characteristics"] = {} - chars = data["characteristics"] - try: - if config["timeout"]: - alarm(config["timeout"]) - - if ("discriminant" not in chars or config["recompute"]) and (not config["only"] or "discriminant" in config["only"]): - info("Computing discriminant...", 1, err=True) - chars["discriminant"] = str(to_int(curve.discriminant())) - - if ("j_invariant" not in chars or config["recompute"]) and (not config["only"] or "j_invariant" in config["only"]): - info("Computing j-invariant...", 1, err=True) - chars["j_invariant"] = str(to_int(curve.j_invariant())) - - if ("trace_of_frobenius" not in chars or config["recompute"]) and (not config["only"] or "trace_of_frobenius" in config["only"]): - info("Computing trace of Frobenius...", 1, err=True) - chars["trace_of_frobenius"] = str(int(curve.trace_of_frobenius())) - - if ("embedding_degree" not in chars or config["recompute"]) and curve.base_field().characteristic() != 2 and (not config["only"] or "embedding_degree" in config["only"]): - info("Computing embedding degree...", 1, err=True) - chars["embedding_degree"] = str(int(GF(generator.order())(curve.base_field().characteristic()).multiplicative_order())) - - if ("anomalous" not in chars or config["recompute"]) and (not config["only"] or "anomalous" in config["only"]): - info("Checking anomalous condition...", 1, err=True) - chars["anomalous"] = curve.base_field().characteristic() == generator.order() - - if ("supersingular" not in chars or config["recompute"]) and (not config["only"] or "supersingular" in config["only"]): - info("Checking supersingular condition...", 1, err=True) - chars["supersingular"] = curve.is_supersingular() - - if ("cm_disc" not in chars or config["recompute"]) and (not config["only"] or "cm_disc" in config["only"]): - info("Computing CM-discriminant...", 1, err=True) - chars["cm_disc"] = str(int(squarefree_part(4 * curve.base_field().order() - curve.trace_of_frobenius()))) - - if ("conductor" not in chars or config["recompute"]) and (not config["only"] or "conductor" in config["only"]): - info("Computing conductor...", 1, err=True) - chars["conductor"] = str(int(sqrt((4 * curve.base_field().order() - curve.trace_of_frobenius())/squarefree_part(4 * curve.base_field().order() - curve.trace_of_frobenius())))) - - if ("torsion_degrees" not in chars or config["recompute"]) and curve.base_field().characteristic() != 2 and (not config["only"] or "torsion_degrees" in config["only"]): - info("Computing torsion extension degrees...", 1, err=True) - degs = [] - chars["torsion_degrees"] = degs - for r in primes(20): - info(f"\t r={r}", 2, err=True) - least, full = find_torsions(curve, r) - degs.append({"r": int(r), "least": int(least), "full": int(full)}) - cancel_alarm() - except AlarmInterrupt: - click.secho("! Timeout reached !", fg="red", err=True) - info(f"[{data['name']}] * End * ", 0, fg="green", bold=True, err=True) - return data - - -@click.command(help="Elliptic Curve analysis script. Computes various interesting characteristics of elliptic curves over finite fields.", - epilog="© 2020 Jan Jancar, Vladimir Sedlacek: MIT licensed") -@click.option('-v', '--verbose', count=True, help="Set the verbosity of output. Can be used multiple times to increase verbosity.") -@click.option('-o', '--only', multiple=True, type=str, help="Only compute a particular characteristic and skip others.") -@click.option('-r', '--recompute', is_flag=True, help="Force recomputation of characteristics present in input.") -@click.option('-c', '--curves', multiple=True, type=str, help="Only compute characteristics for curves which match the name.") -@click.option('-t', '--timeout', type=int, help="Limit computation time to a given value in seconds, will output partial results.") -def main(**kwargs): - global config - config = copy(kwargs) - data = json.load(sys.stdin) - if "curves" in data: - data["curves"] = [analyze(curve) for curve in data["curves"]] - else: - data = analyze(data) - click.echo(json.dumps(data, indent=2)) - - -if __name__ == "__main__": - main()