diff --git a/lib/Crypto/PublicKey/_montgomery.py b/lib/Crypto/PublicKey/_montgomery.py index 0d90dbf4..399c1020 100644 --- a/lib/Crypto/PublicKey/_montgomery.py +++ b/lib/Crypto/PublicKey/_montgomery.py @@ -3,7 +3,8 @@ from ._curve import _Curve from Crypto.Math.Numbers import Integer -from Crypto.Util._raw_api import load_pycryptodome_raw_lib +from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, + SmartPointer) def curve25519_curve(): @@ -15,7 +16,8 @@ def curve25519_curve(): int curve25519_new_point(Point **out, const uint8_t x[32], - size_t modsize); + size_t modsize, + const void* context); int curve25519_clone(Point **P, const Point *Q); void curve25519_free_point(Point *p); int curve25519_get_x(uint8_t *xb, size_t modsize, Point *p); @@ -44,3 +46,54 @@ class EcLib(object): None, EcLib) return curve25519 + + +def curve448_curve(): + p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff # 2**448 - 2**224 - 1 + order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 + + _curve448_lib = load_pycryptodome_raw_lib("Crypto.PublicKey._curve448", """ +typedef void Curve448Context; +typedef void Curve448Point; + +int curve448_new_context(Curve448Context **pec_ctx); +void curve448_free_context(Curve448Context *ec_ctx); +int curve448_new_point(Curve448Point **out, + const uint8_t *x, + size_t len, + const Curve448Context *ec_ctx); +void curve448_free_point(Curve448Point *p); +int curve448_clone(Curve448Point **P, const Curve448Point *Q); +int curve448_get_x(uint8_t *xb, size_t modsize, const Curve448Point *p); +int curve448_scalar(Curve448Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); +int curve448_cmp(const Curve448Point *ecp1, const Curve448Point *ecp2); +""") + + class EcLib(object): + new_context = _curve448_lib.curve448_new_context + free_context = _curve448_lib.curve448_free_context + new_point = _curve448_lib.curve448_new_point + clone = _curve448_lib.curve448_clone + free_point = _curve448_lib.curve448_free_point + get_x = _curve448_lib.curve448_get_x + scalar = _curve448_lib.curve448_scalar + cmp = _curve448_lib.curve448_cmp + + curve448_context = VoidPointer() + result = EcLib.new_context(curve448_context.address_of()) + if result: + raise ImportError("Error %d initializing Curve448 context" % result) + + curve448 = _Curve(Integer(p), + None, + Integer(order), + Integer(9), + None, + None, + 448, + "1.3.101.111", # RFC8410 + SmartPointer(curve448_context.get(), EcLib.free_context), + "Curve448", + None, + EcLib) + return curve448 diff --git a/lib/Crypto/PublicKey/_point.py b/lib/Crypto/PublicKey/_point.py index ba407578..43b4ef8d 100644 --- a/lib/Crypto/PublicKey/_point.py +++ b/lib/Crypto/PublicKey/_point.py @@ -20,6 +20,7 @@ class CurveID(object): ED25519 = 6 ED448 = 7 CURVE25519 = 8 + CURVE448 = 9 class _Curves(object): @@ -40,8 +41,10 @@ class _Curves(object): ed25519_names = ["ed25519", "Ed25519"] ed448_names = ["ed448", "Ed448"] curve25519_names = ["curve25519", "Curve25519", "X25519"] + curve448_names = ["curve448", "Curve448", "X448"] - all_names = p192_names + p224_names + p256_names + p384_names + p521_names + ed25519_names + ed448_names + curve25519_names + all_names = p192_names + p224_names + p256_names + p384_names + p521_names + \ + ed25519_names + ed448_names + curve25519_names + curve448_names def __contains__(self, item): return item in self.all_names @@ -90,6 +93,11 @@ def load(self, name): curve25519 = _montgomery.curve25519_curve() curve25519.id = CurveID.CURVE25519 self.curves.update(dict.fromkeys(self.curve25519_names, curve25519)) + elif name in self.curve448_names: + from . import _montgomery + curve448 = _montgomery.curve448_curve() + curve448.id = CurveID.CURVE448 + self.curves.update(dict.fromkeys(self.curve448_names, curve448)) else: raise ValueError("Unsupported curve '%s'" % name) return self.curves[name] @@ -99,12 +107,13 @@ def __getitem__(self, name): curve = self.curves.get(name) if curve is None: curve = self.load(name) - if name in self.curve25519_names: + if name in self.curve25519_names or name in self.curve448_names: curve.G = EccXPoint(curve.Gx, name) else: curve.G = EccPoint(curve.Gx, curve.Gy, name) curve.is_edwards = curve.id in (CurveID.ED25519, CurveID.ED448) - curve.is_montgomery = curve.id in (CurveID.CURVE25519,) + curve.is_montgomery = curve.id in (CurveID.CURVE25519, + CurveID.CURVE448) curve.is_weierstrass = not (curve.is_edwards or curve.is_montgomery) return curve @@ -354,22 +363,30 @@ def __init__(self, x, curve): raise ValueError("Unknown curve name %s" % str(curve)) self.curve = self._curve.canonical - if self._curve.id != CurveID.CURVE25519: - raise ValueError("EccXPoint can only be created for Curve25519") + if self._curve.id not in (CurveID.CURVE25519, CurveID.CURVE448): + raise ValueError("EccXPoint can only be created for Curve25519/Curve448") modulus_bytes = self.size_in_bytes() - xb = long_to_bytes(x, modulus_bytes) - if len(xb) != modulus_bytes: - raise ValueError("Incorrect coordinate length") + if x is not None: + xb = c_uint8_ptr(long_to_bytes(x, modulus_bytes)) + if len(xb) != modulus_bytes: + raise ValueError("Incorrect coordinate length") new_point = self._curve.rawlib.new_point free_func = self._curve.rawlib.free_point + self._point = VoidPointer() + try: + context = self._curve.context.get() + except AttributeError: + context = null_pointer + self._point = VoidPointer() result = new_point(self._point.address_of(), - c_uint8_ptr(xb), - c_size_t(modulus_bytes)) + c_uint8_ptr(xb) if x is not None else null_pointer, + c_size_t(modulus_bytes), + context) if result == 15: raise ValueError("The EC point does not belong to the curve") @@ -424,7 +441,7 @@ def is_point_at_infinity(self): def point_at_infinity(self): """Return the *point-at-infinity* for the curve.""" - return EccXPoint(self._curve.Gx, self.curve) * 0 + return EccXPoint(None, self.curve) @property def x(self): diff --git a/setup.py b/setup.py index 9214c655..2bbe833f 100644 --- a/setup.py +++ b/setup.py @@ -451,6 +451,11 @@ def create_cryptodome_lib(): sources=['src/curve25519.c'], py_limited_api=True, ), + Extension("Crypto.PublicKey._curve448", + include_dirs=['src/'], + sources=['src/curve448.c', 'src/mont1.c'], + py_limited_api=True, + ), Extension("Crypto.PublicKey._ed25519", include_dirs=['src/'], sources=['src/ed25519.c'], @@ -458,14 +463,14 @@ def create_cryptodome_lib(): ), Extension("Crypto.PublicKey._ed448", include_dirs=['src/'], - sources=['src/ed448.c', 'src/mont1.c'], + sources=['src/ed448.c', 'src/mont2.c'], py_limited_api=True, ), # Math Extension("Crypto.Math._modexp", include_dirs=['src/'], - sources=['src/modexp.c', 'src/mont2.c'], + sources=['src/modexp.c', 'src/mont3.c'], py_limited_api=True, ), ] diff --git a/src/Makefile b/src/Makefile index b8917766..73e095af 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,7 @@ CFLAGS=-Werror -Wall -O3 -g -Wno-unused-const-variable -Wconversion -Wsign-conve CFLAGS += -fanalyzer -all:: modexp ec_ws_p256 ec_ws_p384 ec_ws_p521 ed25519_perf ed448_perf curve25519_perf +all:: modexp ec_ws_p256 ec_ws_p384 ec_ws_p521 ed25519_perf ed448_perf curve25519_perf curve448_perf ec_ws_p256: ec_ws_p256.c mont.c p256_table.c p384_table.c p521_table.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DMAIN @@ -39,5 +39,8 @@ p521_table.c: make_ecc_table.py curve25519_perf: curve25519.c multiply_64.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DPROFILE +curve448_perf: curve448.c mont.c + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DPROFILE + clean:: - rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp x25519 ed25519_perf ed448_perf curve25519_perf + rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp x25519 ed25519_perf ed448_perf curve25519_perf curve448_perf diff --git a/src/curve25519.c b/src/curve25519.c index 014d271a..e2b31cb7 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -154,20 +154,29 @@ STATIC void curve25519_scalar_internal(Point *Pout, EXPORT_SYM int curve25519_new_point(Point **out, const uint8_t x[32], - size_t modsize) + size_t modsize, + const void *context) { - if ((NULL == out) || (NULL == x)) + if (NULL == out) return ERR_NULL; - if (modsize != 32) + if (context != NULL) + return ERR_UNKNOWN; + + if ((modsize != 32) && (modsize != 0)) return ERR_MODULUS; *out = calloc(1, sizeof(Point)); if (NULL == *out) return ERR_MEMORY; - convert_be8_to_le25p5((*out)->X, x); - (*out)->Z[0] = 1; + if ((x != NULL) && (modsize == 32)) { + convert_be8_to_le25p5((*out)->X, x); + (*out)->Z[0] = 1; + } else { + /** PAI **/ + (*out)->X[0] = 1; + } /* No need to verify if the point is on the Curve25519 curve */ diff --git a/src/curve25519.h b/src/curve25519.h index db7a70b3..a60e00ae 100644 --- a/src/curve25519.h +++ b/src/curve25519.h @@ -8,7 +8,8 @@ typedef struct Point { EXPORT_SYM int curve25519_new_point(Point **out, const uint8_t x[32], - size_t modsize); + size_t modsize, + const void *context); EXPORT_SYM int curve25519_clone(Point **P, const Point *Q); EXPORT_SYM void curve25519_free_point(Point *p); EXPORT_SYM int curve25519_get_x(uint8_t *xb, size_t modsize, const Point *p); diff --git a/src/curve448.c b/src/curve448.c new file mode 100644 index 00000000..f7851d71 --- /dev/null +++ b/src/curve448.c @@ -0,0 +1,482 @@ +/* Copyright (C) 2024 by Helder Eijs -- All rights reserved. + * This code is licensed under the BSD 2-Clause license. See LICENSE.rst file for info. */ + +/* + * curve448 is a Montgomery curve with equation: + * + * y² = x³ + Ax² + x + * + * over the prime field 2⁴⁴⁸ - 2²²⁴ - 1 with A = 156326. + * It has cofactor 4 and order 2⁴⁴⁶ - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d. + * Also, it is birationally equivalent to the untwisted Edwards curve ed448. + * + * A point is represented by coordinates (X, Z) so that x = X/Z for a + * non-zero Z, with two possible y-coordinates. + * + * In this implementation, Z is always 1. + * + * The PAI (or neutral point) is (X, 0). + */ + +#include "common.h" +#include "endianess.h" +#include "mont.h" +#include "curve448.h" + +FAKE_INIT(curve448) + +STATIC void free_workplace(WorkplaceCurve448 *wp) +{ + if (wp) { + free(wp->a); + free(wp->b); + free(wp->scratch); + free(wp); + } +} + +STATIC WorkplaceCurve448 *new_workplace(const MontContext *ctx) +{ + WorkplaceCurve448 *wp = NULL; + int res; + + wp = calloc(1, sizeof(WorkplaceCurve448)); + if (NULL == wp) + return NULL; + + res = mont_new_number(&wp->a, 1, ctx); + if (res) goto cleanup; + res = mont_new_number(&wp->b, 1, ctx); + if (res) goto cleanup; + res = mont_new_number(&wp->scratch, SCRATCHPAD_NR, ctx); + if (res) goto cleanup; + return wp; + +cleanup: + free_workplace(wp); + return NULL; +} + +/* + * Swap arguments a/c and b/d when condition is NOT ZERO. + * If the condition IS ZERO, no swapping takes place. + */ +STATIC void cswap(uint64_t a[7], uint64_t b[7], uint64_t c[7], uint64_t d[7], unsigned swap) +{ + uint64_t mask, i, e, f; + + mask = (uint64_t)(0 - (swap!=0)); /* 0 if swap is 0, all 1s if swap is !=0 */ + for (i=0; i<7; i++) { + e = mask & (a[i] ^ c[i]); + a[i] ^= e; + c[i] ^= e; + f = mask & (b[i] ^ d[i]); + b[i] ^= f; + d[i] ^= f; + } +} + +/* + * Perform a step of the Montgomery ladder. + * It is based on the function f() such that: + * + * P_{m+n} = f(P_m, P_n, P_{n-m}) + * + * limited to the cases: + * + * P_{2n} = f(P_n, P_n, P_0) + * P_{2n+1} = f(P_n, P_{n+1}, P_1) + * + * so that: + * + * P_2 = f(P_1, P_1, P_0) + * P_3 = f(P_1, P_2, P_1) + * P_4 = f(P_2, P_2, P_0) + * P_5 = f(P_2, P_3, P_1) + * P_6 = f(P_3, P_3, P_0) + * ... + * + * Here we efficiently combine two computations of f(): + * + * P_{2n} = f(P_n, P_n, P_0) + * P_{2n+1} = f(P_n, P_{n+1}, P_1) + * + * Ref: https://eprint.iacr.org/2017/293.pdf + * + * @param[in,out] P2 In input, the point to double. + * In output, the double of the original P2. + * @param[in,out] P3 In input, the point to double (P2) + P1. + * In output, the double of the original P2 + P1. + * @param[in] P1 The fixed P1 point (Z1=1). + */ +STATIC void curve448_ladder_step(Curve448Point *P2, Curve448Point *P3, const Curve448Point *P1) +{ + const MontContext *ctx = P2->ec_ctx->mont_ctx; + const uint64_t *a24 = P2->ec_ctx->a24; + uint64_t *t0 = P2->wp->a; + uint64_t *t1 = P2->wp->b; + uint64_t *x2 = P2->x; + uint64_t *z2 = P2->z; + uint64_t *x3 = P3->x; + uint64_t *z3 = P3->z; + const uint64_t *xp = P1->x; + uint64_t *scratch = P2->wp->scratch; + + /** https://www.hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#ladder-mladd-1987-m **/ + + mont_sub(t0, x3, z3, scratch, ctx); /* t0 = D = X3 - Z3 */ + mont_sub(t1, x2, z2, scratch, ctx); /* t1 = B = X2 - Z2 */ + mont_add(x2, x2, z2, scratch, ctx); /* x2 = A = X2 + Z2 */ + mont_add(z2, x3, z3, scratch, ctx); /* z2 = C = X3 - Z3 */ + mont_mult(z3, t0, x2, scratch, ctx); /* z3 = DA */ + mont_mult(z2, z2, t1, scratch, ctx); /* z2 = CB */ + mont_add(x3, z3, z2, scratch, ctx); /* x3 = DA+CB */ + mont_sub(z2, z3, z2, scratch, ctx); /* z2 = DA-CB */ + mont_mult(x3, x3, x3, scratch, ctx); /* x3 = X5 = (DA+CB)² */ + mont_mult(z2, z2, z2, scratch, ctx); /* z2 = (DA-CB)² */ + mont_mult(t0, t1, t1, scratch, ctx); /* t0 = BB = B² */ + mont_mult(t1, x2, x2, scratch, ctx); /* t1 = AA = A² */ + mont_sub(x2, t1, t0, scratch, ctx); /* x2 = E = AA-BB */ + mont_mult(z3, xp, z2, scratch, ctx); /* z3 = Z5 = X1*(DA-CB)² */ + mont_mult(z2, a24, x2, scratch, ctx); /* z2 = a24*E */ + mont_add(z2, t0, z2, scratch, ctx); /* z2 = BB+a24*E */ + mont_mult(z2, x2, z2, scratch, ctx); /* z2 = Z4 = E*(BB+a24*E) */ + mont_mult(x2, t1, t0, scratch, ctx); /* x2 = X4 = AA*BB */ +} + +/* + * Scalar multiplication Q = k*B + * + * @param[out] Pout The output point Q. + * @param[in] k The scalar encoded in big-endian mode. + * @param[in] len Length of the scalar in bytes. + * @param[in] Pin The input point B. + */ +STATIC int curve448_scalar_internal(Curve448Point *Pout, + const uint8_t *k, + size_t len, + const Curve448Point *Pin) +{ + Curve448Point *P2 = NULL; + Curve448Point *P3 = NULL; + const Curve448Context *ec_ctx = Pin->ec_ctx; + const MontContext *mont_ctx = ec_ctx->mont_ctx; + unsigned bit_idx, swap; + size_t scan; + int res; + + /* P2 = PAI */ + res = curve448_new_point(&P2, NULL, 0, Pin->ec_ctx); + if (res) goto cleanup; + + /* P3 = Pin */ + res = curve448_clone(&P3, Pin); + if (res) goto cleanup; + + /* + * https://eprint.iacr.org/2020/956.pdf + * https://www.ams.org/journals/mcom/1987-48-177/S0025-5718-1987-0866113-7/S0025-5718-1987-0866113-7.pdf + */ + + /* Scan all bits from MSB to LSB */ + bit_idx = 7; + swap = 0; + scan = 0; + while (scan> bit_idx) & 1; + swap ^= bit; + cswap(P2->x, P2->z, P3->x, P3->z, swap); + curve448_ladder_step(P2, P3, Pin); + swap = bit; + if (bit_idx-- == 0) { + bit_idx = 7; + scan++; + } + } + cswap(P2->x, P2->z, P3->x, P3->z, swap); + + /* P2 is the result */ + + if (mont_is_zero(P2->z, mont_ctx)) { + mont_set(Pout->x, 1, mont_ctx); + mont_set(Pout->z, 0, mont_ctx); + } else { + uint64_t *invz = Pout->wp->a; + + /** TODO: replace with add chain **/ + res = mont_inv_prime(invz, P2->z, mont_ctx); + if (res) goto cleanup; + res = mont_mult(Pout->x, P2->x, invz, P2->wp->scratch, mont_ctx); + if (res) goto cleanup; + mont_set(Pout->z, 1, mont_ctx); + } + res = 0; + +cleanup: + curve448_free_point(P2); + curve448_free_point(P3); + return res; +} + +/* ------------------------------------- */ + +/* + * Create an Elliptic Curve context for Curve448 + * + * @param pec_ctx The memory area where the pointer to the newly allocated + * EC context will be stored. + * @return 0 for success, the appropriate error code otherwise + */ +EXPORT_SYM int curve448_new_context(Curve448Context **pec_ctx) +{ + Curve448Context *ec_ctx = NULL; + int res; + MontContext *ctx; + const uint8_t mod448_be[56] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + if (NULL == pec_ctx) + return ERR_NULL; + + *pec_ctx = ec_ctx = (Curve448Context*)calloc(1, sizeof(Curve448Context)); + if (NULL == ec_ctx) + return ERR_MEMORY; + + res = mont_context_init(&ec_ctx->mont_ctx, mod448_be, sizeof(mod448_be)); + if (res) goto cleanup; + ctx = ec_ctx->mont_ctx; + + /* a24 = (a+2)/4 */ + res = mont_new_from_uint64(&ec_ctx->a24, 39082, ctx); + if (res) goto cleanup; + + return 0; + +cleanup: + free(ec_ctx->a24); + mont_context_free(ec_ctx->mont_ctx); + free(ec_ctx); + return res; +} + +EXPORT_SYM void curve448_free_context(Curve448Context *ec_ctx) +{ + if (NULL != ec_ctx) { + free(ec_ctx->a24); + mont_context_free(ec_ctx->mont_ctx); + free(ec_ctx); + } +} + +/* + * Create a new EC point on the Curve448 curve. + * + * @param out The memory area where the pointer to the newly allocated EC + * point will be stored. + * Use curve448_free_point() for deallocating it. + * @param x The X-coordinate (affine, big-endian, smaller than modulus) + * @param len The length of x in bytes (max 56 bytes) + * @param ec_ctx The EC context + * @return 0 for success, the appropriate error code otherwise + * + * If x is NULL or len is 0, the point will be the point at infinity. + */ +EXPORT_SYM int curve448_new_point(Curve448Point **out, + const uint8_t *x, + size_t len, + const Curve448Context *ec_ctx) +{ + int res; + Curve448Point *ecp = NULL; + const MontContext *mont_ctx; + + if (NULL == out || NULL == ec_ctx) + return ERR_NULL; + + if (len > ec_ctx->mont_ctx->bytes) + return ERR_VALUE; + + *out = ecp = (Curve448Point*)calloc(1, sizeof(Curve448Point)); + if (NULL == ecp) + return ERR_MEMORY; + + ecp->ec_ctx = (Curve448Context*) ec_ctx; + mont_ctx = ec_ctx->mont_ctx; + + if ((NULL == x) || (0 == len)) { + res = mont_new_from_uint64(&ecp->x, 1, mont_ctx); + if (res) goto cleanup; + res = mont_new_from_uint64(&ecp->z, 0, mont_ctx); + if (res) goto cleanup; + } else { + res = mont_new_from_bytes(&ecp->x, x, len, mont_ctx); + if (res) goto cleanup; + res = mont_new_from_uint64(&ecp->z, 1, mont_ctx); + if (res) goto cleanup; + } + + ecp->wp = new_workplace(mont_ctx); + if (NULL == ecp->wp) { + res = ERR_MEMORY; + goto cleanup; + } + + /* No need to verify if the point is on the Curve448 curve */ + return 0; + +cleanup: + free(ecp->x); + free(ecp->z); + free(ecp->wp); + free(ecp); + *out = NULL; + return res; +} + +EXPORT_SYM void curve448_free_point(Curve448Point *ecp) +{ + /* The EC context (ecp->ecp_ctx) is allocated once and shared by all + * points on the same surve, so we will not free it here. + */ + if (ecp) { + free_workplace(ecp->wp); + free(ecp->x); + free(ecp->z); + free(ecp); + } +} + +EXPORT_SYM int curve448_clone(Curve448Point **pecp2, const Curve448Point *ecp) +{ + int res = -1; + Curve448Point *ecp2; + MontContext *ctx; + + if (NULL == pecp2 || NULL == ecp) + return ERR_NULL; + ctx = ecp->ec_ctx->mont_ctx; + + *pecp2 = ecp2 = (Curve448Point*)calloc(1, sizeof(Curve448Point)); + if (NULL == ecp2) + return ERR_MEMORY; + + ecp2->ec_ctx = ecp->ec_ctx; + + ecp2->wp = new_workplace(ctx); + if (NULL == ecp2->wp) goto cleanup; + + res = mont_new_number(&ecp2->x, 1, ctx); + if (res) goto cleanup; + res = mont_copy(ecp2->x, ecp->x, ctx); + if (res) goto cleanup; + + res = mont_new_number(&ecp2->z, 1, ctx); + if (res) goto cleanup; + res = mont_copy(ecp2->z, ecp->z, ctx); + if (res) goto cleanup; + + return 0; + +cleanup: + free_workplace(ecp2->wp); + free(ecp2->x); + free(ecp2->z); + free(ecp2); + *pecp2 = NULL; + return res; +} + +EXPORT_SYM int curve448_get_x(uint8_t *xb, size_t modsize, const Curve448Point *p) +{ + MontContext *mont_ctx; + + if ((NULL == xb) || (NULL == p)) + return ERR_NULL; + + mont_ctx = p->ec_ctx->mont_ctx; + + if (modsize != 56) + return ERR_MODULUS; + + if (mont_is_zero(p->z, mont_ctx)) + return ERR_EC_PAI; + + /** p->Z == 1 **/ + + return mont_to_bytes(xb, modsize, p->x, mont_ctx); +} + +EXPORT_SYM int curve448_scalar(Curve448Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed) +{ + if ((NULL == P) || (NULL == scalar)) + return ERR_NULL; + + curve448_scalar_internal(P, scalar, scalar_len, P); + return 0; +} + +EXPORT_SYM int curve448_cmp(const Curve448Point *p1, const Curve448Point *p2) +{ + MontContext *ctx; + WorkplaceCurve448 *wp; + uint64_t *scratch; + int res; + + if (NULL == p1 || NULL == p2) + return ERR_NULL; + + if (p1->ec_ctx != p2->ec_ctx) + return ERR_EC_CURVE; + + ctx = p1->ec_ctx->mont_ctx; + wp = p1->wp; + scratch = wp->scratch; + + mont_mult(wp->a, p1->x, p2->z, scratch, ctx); + mont_mult(wp->b, p1->z, p2->x, scratch, ctx); + res = mont_is_equal(wp->a, wp->b, ctx); + + return res ? 0 : ERR_VALUE; +} + +#ifdef PROFILE +int main(void) +{ + uint8_t pubkey[56]; + uint8_t secret[56]; + unsigned i; + Curve448Context *ec_ctx; + Curve448Point *Pin = NULL; + Curve448Point *Pout = NULL; + int res; + + secret[0] = pubkey[0] = 0xAA; + for (i=1; i<56; i++) { + secret[i] = pubkey[i] = (uint8_t)((secret[i-1] << 1) | (secret[i-1] >> 7)); + } + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + res = curve448_new_point(&Pin, pubkey, 56, ec_ctx); + assert(res == 0); + + res = curve448_new_point(&Pout, NULL, 56, ec_ctx); + assert(res == 0); + + for (i=0; i<10000; i++) { + res = curve448_scalar_internal(Pout, secret, sizeof secret, Pin); + } + + curve448_free_point(Pin); + curve448_free_point(Pout); + curve448_free_context(ec_ctx); +} +#endif diff --git a/src/curve448.h b/src/curve448.h new file mode 100644 index 00000000..754299b2 --- /dev/null +++ b/src/curve448.h @@ -0,0 +1,35 @@ +#ifndef _CURVE448_H +#define _CURVE448_H + +#include "mont.h" + +typedef struct _WorkplaceCurve448 { + uint64_t *a, *b; + uint64_t *scratch; +} WorkplaceCurve448; + +typedef struct _Curve448Context { + MontContext *mont_ctx; + uint64_t *a24; /* encoded in Montgomery form */ +} Curve448Context; + +typedef struct Curve448Point { + Curve448Context *ec_ctx; + WorkplaceCurve448 *wp; + uint64_t *x; + uint64_t *z; +} Curve448Point; + +EXPORT_SYM int curve448_new_context(Curve448Context **pec_ctx); +EXPORT_SYM void curve448_free_context(Curve448Context *ec_ctx); +EXPORT_SYM int curve448_new_point(Curve448Point **out, + const uint8_t *x, + size_t len, + const Curve448Context *ec_ctx); +EXPORT_SYM void curve448_free_point(Curve448Point *p); +EXPORT_SYM int curve448_clone(Curve448Point **P, const Curve448Point *Q); +EXPORT_SYM int curve448_get_x(uint8_t *xb, size_t modsize, const Curve448Point *p); +EXPORT_SYM int curve448_scalar(Curve448Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); +EXPORT_SYM int curve448_cmp(const Curve448Point *ecp1, const Curve448Point *ecp2); + +#endif diff --git a/src/mont3.c b/src/mont3.c new file mode 100644 index 00000000..558bcad6 --- /dev/null +++ b/src/mont3.c @@ -0,0 +1,6 @@ +/* + This file is used to workaround a bug in distutils that causes race + conditions when the same source file is used in multiple extensions. + */ + +#include "mont.c" diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 828b711b..d8ad392d 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -196,6 +196,10 @@ add_test(NAME test_ed25519 COMMAND test_ed25519) add_executable(test_ed448 test_ed448.c ../ed448.c $) add_test(NAME test_ed448 COMMAND test_ed448) +# curve448 +add_executable(test_curve448 test_curve448.c ../curve448.c $) +add_test(NAME test_curve448 COMMAND test_curve448) + # aesni if (AESNI) add_executable(test_aesni test_aesni.c ../AESNI.c) diff --git a/src/test/test_curve25519.c b/src/test/test_curve25519.c index bb16e599..27532643 100644 --- a/src/test/test_curve25519.c +++ b/src/test/test_curve25519.c @@ -43,7 +43,7 @@ void test_ladder_1(void) Point *Pin; Point Pout; - curve25519_new_point(&Pin, pubkey, 32); + curve25519_new_point(&Pin, pubkey, 32, NULL); /* Clamping BE/LE */ scalar[31-0] &= 248; diff --git a/src/test/test_curve448.c b/src/test/test_curve448.c new file mode 100644 index 00000000..d31270b6 --- /dev/null +++ b/src/test/test_curve448.c @@ -0,0 +1,178 @@ +#include "endianess.h" +#include "curve448.h" +#include "mont.h" +#include + +#if 0 +void print_point(Curve448Point *p) +{ + mont_printf("X=", p->x, p->ec_ctx->mont_ctx); + mont_printf("Z=", p->z, p->ec_ctx->mont_ctx); +} +#endif + +void test_ladder_1(void) +{ + Curve448Context *ec_ctx; + Curve448Point *point; + int res; + + const uint8_t x[56] = { 0x86, 0xa0, 0xf8, 0x4e, 0xfb, 0xa7, 0xa7, 0x8a, + 0xa1, 0xad, 0x94, 0xdb, 0x29, 0x54, 0xfa, 0x83, + 0x25, 0xda, 0xc6, 0x19, 0x8c, 0xc3, 0xbd, 0xdd, + 0x31, 0xc0, 0x4d, 0x81, 0xf9, 0x08, 0x0f, 0x02, + 0x7f, 0x43, 0x07, 0xbd, 0x4c, 0x33, 0x88, 0xad, + 0x8a, 0x3f, 0x26, 0xd5, 0xf2, 0x6c, 0x5f, 0xda, + 0xbf, 0x87, 0x34, 0xfa, 0x40, 0xe6, 0xfc, 0x06 }; + + const uint8_t scalar[] = { 0xd3, 0x0a, 0x60, 0x1c, 0x4f, 0x9a, 0x25, 0x29, + 0x4b, 0xf5, 0x68, 0xa3, 0xeb, 0x43, 0x49, 0xf4, + 0xbf, 0x8f, 0xd7, 0xcd, 0xf8, 0x24, 0x4c, 0x98, + 0x9c, 0x77, 0x0a, 0x70, 0x21, 0xe1, 0xaa, 0xd1, + 0xd0, 0x04, 0x51, 0x04, 0xef, 0xac, 0x82, 0x88, + 0xd2, 0x34, 0x9a, 0xa1, 0xfe, 0x66, 0x52, 0x49, + 0x88, 0x8e, 0xec, 0xf9, 0xdd, 0x2f, 0x26, 0x3c }; + + const uint8_t expected[56] = { 0x6f, 0x6b, 0xd9, 0x3d, 0xf7, 0x82, 0x62, 0x76, + 0x21, 0x1e, 0x11, 0x61, 0x39, 0x22, 0x98, 0x9d, + 0x77, 0xb0, 0x01, 0x6a, 0xc6, 0x5f, 0x44, 0xeb, + 0xad, 0xba, 0x4f, 0xe1, 0x9f, 0x23, 0x5f, 0x6d, + 0x54, 0xd7, 0x12, 0x24, 0x0a, 0xb5, 0x79, 0xdf, + 0xfb, 0x6a, 0x5e, 0xd8, 0xb1, 0x1d, 0xda, 0x97, + 0x66, 0xdc, 0x60, 0x5a, 0xf9, 0x4f, 0x3e, 0xce }; + + uint8_t result_x[56]; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + res = curve448_new_point(&point, x, sizeof(x), ec_ctx); + assert(res == 0); + + res = curve448_scalar(point, scalar, sizeof(scalar), 0); + + assert(mont_is_one(point->z, point->ec_ctx->mont_ctx)); + res = mont_to_bytes(result_x, sizeof(result_x), point->x, point->ec_ctx->mont_ctx); + assert(res == 0); + assert(memcmp(expected, result_x, sizeof(expected)) == 0); + + curve448_free_point(point); + curve448_free_context(ec_ctx); +} + +void test_ladder_2(void) +{ + Curve448Context *ec_ctx; + Curve448Point *point; + int res; + uint8_t x[] = { 5 }; + uint8_t scalar[] = { 0 }; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + res = curve448_new_point(&point, x, sizeof(x), ec_ctx); + assert(res == 0); + + res = curve448_scalar(point, scalar, sizeof(scalar), 0); + + /** PAI **/ + assert(mont_is_one(point->x, point->ec_ctx->mont_ctx)); + assert(mont_is_zero(point->z, point->ec_ctx->mont_ctx)); + + curve448_free_point(point); + curve448_free_context(ec_ctx); +} + +void test_cmp_1(void) +{ + uint8_t c1[56] = { 0xd7, 0x41, 0x07, 0x7b, 0xae, 0x25, 0x76, 0x75, + 0xdb, 0xb5, 0x43, 0x55, 0x0d, 0x6f, 0x27, 0xda, + 0x32, 0x89, 0x21, 0xfd, 0xb9, 0x9b, 0xf5, 0x4e, + 0xbe, 0x9d, 0x4d, 0x0b, 0xcc, 0x58, 0xe9, 0x67, + 0xff, 0x6f, 0xd1, 0xe1, 0x18, 0x2b, 0x22, 0x0f, + 0xa0, 0x05, 0x7f, 0x0b, 0x0d, 0x3b, 0xc8, 0x3f, + 0x86, 0xae, 0x38, 0xef, 0xb3, 0x5f, 0x5a, 0x35 }; + + Curve448Context *ec_ctx; + Curve448Point *G, *P; + int res; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + // G = (C1, 1) + res = curve448_new_point(&G, c1, sizeof(c1), ec_ctx); + assert(res == 0); + + // P = (C1*C2, C2) + res = curve448_clone(&P, G); + assert(res == 0); + + mont_set(P->z, 0x12345678U, P->ec_ctx->mont_ctx); + mont_mult(P->x, G->x, P->z, P->wp->scratch, P->ec_ctx->mont_ctx); + + res = curve448_cmp(G, P); + assert(res == 0); + + mont_set(G->z, 2, G->ec_ctx->mont_ctx); + + // G = (C1, 2) + res = curve448_cmp(G, P); + assert(res != 0); + + curve448_free_point(P); + curve448_free_point(G); + curve448_free_context(ec_ctx); +} + +void test_cmp_2(void) +{ + const uint8_t c1[56] = { 0xd7, 0x41, 0x07, 0x7b, 0xae, 0x25, 0x76, 0x75, + 0xdb, 0xb5, 0x43, 0x55, 0x0d, 0x6f, 0x27, 0xda, + 0x32, 0x89, 0x21, 0xfd, 0xb9, 0x9b, 0xf5, 0x4e, + 0xbe, 0x9d, 0x4d, 0x0b, 0xcc, 0x58, 0xe9, 0x67, + 0xff, 0x6f, 0xd1, 0xe1, 0x18, 0x2b, 0x22, 0x0f, + 0xa0, 0x05, 0x7f, 0x0b, 0x0d, 0x3b, 0xc8, 0x3f, + 0x86, 0xae, 0x38, 0xef, 0xb3, 0x5f, 0x5a, 0x35 }; + const uint8_t scalar[] = { 0 }; + + Curve448Context *ec_ctx; + Curve448Point *G, *P, *Q; + int res; + + res = curve448_new_context(&ec_ctx); + assert(res == 0); + + /** 3 different ways to create API **/ + + res = curve448_new_point(&G, 0, sizeof(c1), ec_ctx); + assert(res == 0); + + res = curve448_new_point(&P, c1, 0, ec_ctx); + assert(res == 0); + + res = curve448_new_point(&Q, c1, sizeof(c1), ec_ctx); + assert(res == 0); + res = curve448_scalar(Q, scalar, sizeof(scalar), 0); + assert(res == 0); + + res = curve448_cmp(G, P); + assert(res == 0); + res = curve448_cmp(G, Q); + assert(res == 0); + + curve448_free_point(Q); + curve448_free_point(P); + curve448_free_point(G); + curve448_free_context(ec_ctx); +} + +int main(void) +{ + test_ladder_1(); + test_ladder_2(); + test_cmp_1(); + test_cmp_2(); + return 0; +}