#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "mpi-internal.h"
#include "longlong.h"
#include "g10lib.h"
#include "context.h"
#include "ec-context.h"
#include "ec-internal.h"
#define point_init(a) _gcry_mpi_point_init ((a))
#define point_free(a) _gcry_mpi_point_free_parts ((a))
void
_gcry_mpi_point_log (const char *name, mpi_point_t point, mpi_ec_t ctx)
{
gcry_mpi_t x, y;
char buf[100];
if (!point)
{
snprintf (buf, sizeof buf - 1, "%s.*", name);
log_mpidump (buf, NULL);
return;
}
snprintf (buf, sizeof buf - 1, "%s.X", name);
if (ctx)
{
x = mpi_new (0);
y = mpi_new (0);
}
if (!ctx || _gcry_mpi_ec_get_affine (x, y, point, ctx))
{
log_mpidump (buf, point->x);
buf[strlen(buf)-1] = 'Y';
log_mpidump (buf, point->y);
buf[strlen(buf)-1] = 'Z';
log_mpidump (buf, point->z);
}
else
{
buf[strlen(buf)-1] = 'x';
log_mpidump (buf, x);
buf[strlen(buf)-1] = 'y';
log_mpidump (buf, y);
}
if (ctx)
{
_gcry_mpi_release (x);
_gcry_mpi_release (y);
}
}
mpi_point_t
_gcry_mpi_point_new (unsigned int nbits)
{
mpi_point_t p;
(void)nbits;
p = xmalloc (sizeof *p);
_gcry_mpi_point_init (p);
return p;
}
void
_gcry_mpi_point_release (mpi_point_t p)
{
if (p)
{
_gcry_mpi_point_free_parts (p);
xfree (p);
}
}
void
_gcry_mpi_point_init (mpi_point_t p)
{
p->x = mpi_new (0);
p->y = mpi_new (0);
p->z = mpi_new (0);
}
void
_gcry_mpi_point_free_parts (mpi_point_t p)
{
mpi_free (p->x); p->x = NULL;
mpi_free (p->y); p->y = NULL;
mpi_free (p->z); p->z = NULL;
}
static void
point_set (mpi_point_t d, mpi_point_t s)
{
mpi_set (d->x, s->x);
mpi_set (d->y, s->y);
mpi_set (d->z, s->z);
}
gcry_mpi_point_t
_gcry_mpi_point_copy (gcry_mpi_point_t point)
{
mpi_point_t newpoint;
newpoint = _gcry_mpi_point_new (0);
if (point)
point_set (newpoint, point);
return newpoint;
}
static void
point_resize (mpi_point_t p, mpi_ec_t ctx)
{
size_t nlimbs = 2*ctx->p->nlimbs+1;
mpi_resize (p->x, nlimbs);
if (ctx->model != MPI_EC_MONTGOMERY)
mpi_resize (p->y, nlimbs);
mpi_resize (p->z, nlimbs);
}
static void
point_swap_cond (mpi_point_t d, mpi_point_t s, unsigned long swap,
mpi_ec_t ctx)
{
mpi_swap_cond (d->x, s->x, swap);
if (ctx->model != MPI_EC_MONTGOMERY)
mpi_swap_cond (d->y, s->y, swap);
mpi_swap_cond (d->z, s->z, swap);
}
void
_gcry_mpi_point_get (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z,
mpi_point_t point)
{
if (x)
mpi_set (x, point->x);
if (y)
mpi_set (y, point->y);
if (z)
mpi_set (z, point->z);
}
void
_gcry_mpi_point_snatch_get (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z,
mpi_point_t point)
{
mpi_snatch (x, point->x);
mpi_snatch (y, point->y);
mpi_snatch (z, point->z);
xfree (point);
}
mpi_point_t
_gcry_mpi_point_set (mpi_point_t point,
gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z)
{
if (!point)
point = mpi_point_new (0);
if (x)
mpi_set (point->x, x);
else
mpi_clear (point->x);
if (y)
mpi_set (point->y, y);
else
mpi_clear (point->y);
if (z)
mpi_set (point->z, z);
else
mpi_clear (point->z);
return point;
}
mpi_point_t
_gcry_mpi_point_snatch_set (mpi_point_t point,
gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z)
{
if (!point)
point = mpi_point_new (0);
if (x)
mpi_snatch (point->x, x);
else
mpi_clear (point->x);
if (y)
mpi_snatch (point->y, y);
else
mpi_clear (point->y);
if (z)
mpi_snatch (point->z, z);
else
mpi_clear (point->z);
return point;
}
static void
ec_mod (gcry_mpi_t w, mpi_ec_t ec)
{
if (0 && ec->dialect == ECC_DIALECT_ED25519)
_gcry_mpi_ec_ed25519_mod (w);
else if (ec->t.p_barrett)
_gcry_mpi_mod_barrett (w, w, ec->t.p_barrett);
else
_gcry_mpi_mod (w, w, ec->p);
}
static void
ec_addm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
{
mpi_add (w, u, v);
ec_mod (w, ctx);
}
static void
ec_subm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ec)
{
mpi_sub (w, u, v);
while (w->sign)
mpi_add (w, w, ec->p);
}
static void
ec_mulm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx)
{
mpi_mul (w, u, v);
ec_mod (w, ctx);
}
static void
ec_mul2 (gcry_mpi_t w, gcry_mpi_t u, mpi_ec_t ctx)
{
mpi_lshift (w, u, 1);
ec_mod (w, ctx);
}
static void
ec_powm (gcry_mpi_t w, const gcry_mpi_t b, const gcry_mpi_t e,
mpi_ec_t ctx)
{
mpi_powm (w, b, e, ctx->p);
}
static void
ec_pow2 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
{
ec_mulm (w, b, b, ctx);
}
static void
ec_pow3 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx)
{
mpi_powm (w, b, mpi_const (MPI_C_THREE), ctx->p);
}
static void
ec_invm (gcry_mpi_t x, gcry_mpi_t a, mpi_ec_t ctx)
{
if (!mpi_invm (x, a, ctx->p))
{
log_error ("ec_invm: inverse does not exist:\n");
log_mpidump (" a", a);
log_mpidump (" p", ctx->p);
}
}
void
_gcry_mpi_ec_get_reset (mpi_ec_t ec)
{
ec->t.valid.a_is_pminus3 = 0;
ec->t.valid.two_inv_p = 0;
}
static int
ec_get_a_is_pminus3 (mpi_ec_t ec)
{
gcry_mpi_t tmp;
if (!ec->t.valid.a_is_pminus3)
{
ec->t.valid.a_is_pminus3 = 1;
tmp = mpi_alloc_like (ec->p);
mpi_sub_ui (tmp, ec->p, 3);
ec->t.a_is_pminus3 = !mpi_cmp (ec->a, tmp);
mpi_free (tmp);
}
return ec->t.a_is_pminus3;
}
static gcry_mpi_t
ec_get_two_inv_p (mpi_ec_t ec)
{
if (!ec->t.valid.two_inv_p)
{
ec->t.valid.two_inv_p = 1;
if (!ec->t.two_inv_p)
ec->t.two_inv_p = mpi_alloc (0);
ec_invm (ec->t.two_inv_p, mpi_const (MPI_C_TWO), ec);
}
return ec->t.two_inv_p;
}
static const char *curve25519_bad_points[] = {
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0",
"0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee",
NULL
};
static gcry_mpi_t
scanval (const char *string)
{
gpg_err_code_t rc;
gcry_mpi_t val;
rc = _gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
if (rc)
log_fatal ("scanning ECC parameter failed: %s\n", gpg_strerror (rc));
return val;
}
static void
ec_p_init (mpi_ec_t ctx, enum gcry_mpi_ec_models model,
enum ecc_dialects dialect,
int flags,
gcry_mpi_t p, gcry_mpi_t a, gcry_mpi_t b)
{
int i;
static int use_barrett;
if (!use_barrett)
{
if (getenv ("GCRYPT_BARRETT"))
use_barrett = 1;
else
use_barrett = -1;
}
ctx->model = model;
ctx->dialect = dialect;
ctx->flags = flags;
if (dialect == ECC_DIALECT_ED25519)
ctx->nbits = 256;
else
ctx->nbits = mpi_get_nbits (p);
ctx->p = mpi_copy (p);
ctx->a = mpi_copy (a);
ctx->b = mpi_copy (b);
ctx->t.p_barrett = use_barrett > 0? _gcry_mpi_barrett_init (ctx->p, 0):NULL;
_gcry_mpi_ec_get_reset (ctx);
if (model == MPI_EC_MONTGOMERY)
{
for (i=0; i< DIM(ctx->t.scratch) && curve25519_bad_points[i]; i++)
ctx->t.scratch[i] = scanval (curve25519_bad_points[i]);
}
else
{
for (i=0; i< DIM(ctx->t.scratch); i++)
ctx->t.scratch[i] = mpi_alloc_like (ctx->p);
}
}
static void
ec_deinit (void *opaque)
{
mpi_ec_t ctx = opaque;
int i;
_gcry_mpi_barrett_free (ctx->t.p_barrett);
mpi_free (ctx->p);
mpi_free (ctx->a);
mpi_free (ctx->b);
_gcry_mpi_point_release (ctx->G);
mpi_free (ctx->n);
mpi_free (ctx->h);
_gcry_mpi_point_release (ctx->Q);
mpi_free (ctx->d);
mpi_free (ctx->t.two_inv_p);
for (i=0; i< DIM(ctx->t.scratch); i++)
mpi_free (ctx->t.scratch[i]);
}
mpi_ec_t
_gcry_mpi_ec_p_internal_new (enum gcry_mpi_ec_models model,
enum ecc_dialects dialect,
int flags,
gcry_mpi_t p, gcry_mpi_t a, gcry_mpi_t b)
{
mpi_ec_t ctx;
ctx = xcalloc (1, sizeof *ctx);
ec_p_init (ctx, model, dialect, flags, p, a, b);
return ctx;
}
gpg_err_code_t
_gcry_mpi_ec_p_new (gcry_ctx_t *r_ctx,
enum gcry_mpi_ec_models model,
enum ecc_dialects dialect,
int flags,
gcry_mpi_t p, gcry_mpi_t a, gcry_mpi_t b)
{
gcry_ctx_t ctx;
mpi_ec_t ec;
*r_ctx = NULL;
if (!p || !a)
return GPG_ERR_EINVAL;
ctx = _gcry_ctx_alloc (CONTEXT_TYPE_EC, sizeof *ec, ec_deinit);
if (!ctx)
return gpg_err_code_from_syserror ();
ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
ec_p_init (ec, model, dialect, flags, p, a, b);
*r_ctx = ctx;
return 0;
}
void
_gcry_mpi_ec_free (mpi_ec_t ctx)
{
if (ctx)
{
ec_deinit (ctx);
xfree (ctx);
}
}
gcry_mpi_t
_gcry_mpi_ec_get_mpi (const char *name, gcry_ctx_t ctx, int copy)
{
mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
return _gcry_ecc_get_mpi (name, ec, copy);
}
gcry_mpi_point_t
_gcry_mpi_ec_get_point (const char *name, gcry_ctx_t ctx, int copy)
{
mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
(void)copy;
return _gcry_ecc_get_point (name, ec);
}
gpg_err_code_t
_gcry_mpi_ec_set_mpi (const char *name, gcry_mpi_t newvalue,
gcry_ctx_t ctx)
{
mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
return _gcry_ecc_set_mpi (name, newvalue, ec);
}
gpg_err_code_t
_gcry_mpi_ec_set_point (const char *name, gcry_mpi_point_t newvalue,
gcry_ctx_t ctx)
{
mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
return _gcry_ecc_set_point (name, newvalue, ec);
}
gpg_err_code_t
_gcry_mpi_ec_decode_point (mpi_point_t result, gcry_mpi_t value, mpi_ec_t ec)
{
gcry_err_code_t rc;
if (ec && ec->dialect == ECC_DIALECT_ED25519)
rc = _gcry_ecc_eddsa_decodepoint (value, ec, result, NULL, NULL);
else if (ec && ec->model == MPI_EC_MONTGOMERY)
rc = _gcry_ecc_mont_decodepoint (value, ec, result);
else
rc = _gcry_ecc_os2ec (result, value);
return rc;
}
int
_gcry_mpi_ec_get_affine (gcry_mpi_t x, gcry_mpi_t y, mpi_point_t point,
mpi_ec_t ctx)
{
if (!mpi_cmp_ui (point->z, 0))
return -1;
switch (ctx->model)
{
case MPI_EC_WEIERSTRASS:
{
gcry_mpi_t z1, z2, z3;
z1 = mpi_new (0);
z2 = mpi_new (0);
ec_invm (z1, point->z, ctx);
ec_mulm (z2, z1, z1, ctx);
if (x)
ec_mulm (x, point->x, z2, ctx);
if (y)
{
z3 = mpi_new (0);
ec_mulm (z3, z2, z1, ctx);
ec_mulm (y, point->y, z3, ctx);
mpi_free (z3);
}
mpi_free (z2);
mpi_free (z1);
}
return 0;
case MPI_EC_MONTGOMERY:
{
if (x)
mpi_set (x, point->x);
if (y)
{
log_fatal ("%s: Getting Y-coordinate on %s is not supported\n",
"_gcry_mpi_ec_get_affine", "Montgomery");
return -1;
}
}
return 0;
case MPI_EC_EDWARDS:
{
gcry_mpi_t z;
z = mpi_new (0);
ec_invm (z, point->z, ctx);
if (x)
ec_mulm (x, point->x, z, ctx);
if (y)
ec_mulm (y, point->y, z, ctx);
_gcry_mpi_release (z);
}
return 0;
default:
return -1;
}
}
static void
dup_point_weierstrass (mpi_point_t result, mpi_point_t point, mpi_ec_t ctx)
{
#define x3 (result->x)
#define y3 (result->y)
#define z3 (result->z)
#define t1 (ctx->t.scratch[0])
#define t2 (ctx->t.scratch[1])
#define t3 (ctx->t.scratch[2])
#define l1 (ctx->t.scratch[3])
#define l2 (ctx->t.scratch[4])
#define l3 (ctx->t.scratch[5])
if (!mpi_cmp_ui (point->y, 0) || !mpi_cmp_ui (point->z, 0))
{
mpi_set_ui (x3, 1);
mpi_set_ui (y3, 1);
mpi_set_ui (z3, 0);
}
else
{
if (ec_get_a_is_pminus3 (ctx))
{
ec_pow2 (t1, point->z, ctx);
ec_subm (l1, point->x, t1, ctx);
ec_mulm (l1, l1, mpi_const (MPI_C_THREE), ctx);
ec_addm (t2, point->x, t1, ctx);
ec_mulm (l1, l1, t2, ctx);
}
else
{
ec_pow2 (l1, point->x, ctx);
ec_mulm (l1, l1, mpi_const (MPI_C_THREE), ctx);
ec_powm (t1, point->z, mpi_const (MPI_C_FOUR), ctx);
ec_mulm (t1, t1, ctx->a, ctx);
ec_addm (l1, l1, t1, ctx);
}
ec_mulm (z3, point->y, point->z, ctx);
ec_mul2 (z3, z3, ctx);
ec_pow2 (t2, point->y, ctx);
ec_mulm (l2, t2, point->x, ctx);
ec_mulm (l2, l2, mpi_const (MPI_C_FOUR), ctx);
ec_pow2 (x3, l1, ctx);
ec_mul2 (t1, l2, ctx);
ec_subm (x3, x3, t1, ctx);
ec_pow2 (t2, t2, ctx);
ec_mulm (l3, t2, mpi_const (MPI_C_EIGHT), ctx);
ec_subm (y3, l2, x3, ctx);
ec_mulm (y3, y3, l1, ctx);
ec_subm (y3, y3, l3, ctx);
}
#undef x3
#undef y3
#undef z3
#undef t1
#undef t2
#undef t3
#undef l1
#undef l2
#undef l3
}
static void
dup_point_montgomery (mpi_point_t result, mpi_point_t point, mpi_ec_t ctx)
{
(void)result;
(void)point;
(void)ctx;
log_fatal ("%s: %s not yet supported\n",
"_gcry_mpi_ec_dup_point", "Montgomery");
}
static void
dup_point_edwards (mpi_point_t result, mpi_point_t point, mpi_ec_t ctx)
{
#define X1 (point->x)
#define Y1 (point->y)
#define Z1 (point->z)
#define X3 (result->x)
#define Y3 (result->y)
#define Z3 (result->z)
#define B (ctx->t.scratch[0])
#define C (ctx->t.scratch[1])
#define D (ctx->t.scratch[2])
#define E (ctx->t.scratch[3])
#define F (ctx->t.scratch[4])
#define H (ctx->t.scratch[5])
#define J (ctx->t.scratch[6])
ec_addm (B, X1, Y1, ctx);
ec_pow2 (B, B, ctx);
ec_pow2 (C, X1, ctx);
ec_pow2 (D, Y1, ctx);
if (ctx->dialect == ECC_DIALECT_ED25519)
mpi_sub (E, ctx->p, C);
else
ec_mulm (E, ctx->a, C, ctx);
ec_addm (F, E, D, ctx);
ec_pow2 (H, Z1, ctx);
ec_mul2 (J, H, ctx);
ec_subm (J, F, J, ctx);
ec_subm (X3, B, C, ctx);
ec_subm (X3, X3, D, ctx);
ec_mulm (X3, X3, J, ctx);
ec_subm (Y3, E, D, ctx);
ec_mulm (Y3, Y3, F, ctx);
ec_mulm (Z3, F, J, ctx);
#undef X1
#undef Y1
#undef Z1
#undef X3
#undef Y3
#undef Z3
#undef B
#undef C
#undef D
#undef E
#undef F
#undef H
#undef J
}
void
_gcry_mpi_ec_dup_point (mpi_point_t result, mpi_point_t point, mpi_ec_t ctx)
{
switch (ctx->model)
{
case MPI_EC_WEIERSTRASS:
dup_point_weierstrass (result, point, ctx);
break;
case MPI_EC_MONTGOMERY:
dup_point_montgomery (result, point, ctx);
break;
case MPI_EC_EDWARDS:
dup_point_edwards (result, point, ctx);
break;
}
}
static void
add_points_weierstrass (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
#define x1 (p1->x )
#define y1 (p1->y )
#define z1 (p1->z )
#define x2 (p2->x )
#define y2 (p2->y )
#define z2 (p2->z )
#define x3 (result->x)
#define y3 (result->y)
#define z3 (result->z)
#define l1 (ctx->t.scratch[0])
#define l2 (ctx->t.scratch[1])
#define l3 (ctx->t.scratch[2])
#define l4 (ctx->t.scratch[3])
#define l5 (ctx->t.scratch[4])
#define l6 (ctx->t.scratch[5])
#define l7 (ctx->t.scratch[6])
#define l8 (ctx->t.scratch[7])
#define l9 (ctx->t.scratch[8])
#define t1 (ctx->t.scratch[9])
#define t2 (ctx->t.scratch[10])
if ( (!mpi_cmp (x1, x2)) && (!mpi_cmp (y1, y2)) && (!mpi_cmp (z1, z2)) )
{
_gcry_mpi_ec_dup_point (result, p1, ctx);
}
else if (!mpi_cmp_ui (z1, 0))
{
mpi_set (x3, p2->x);
mpi_set (y3, p2->y);
mpi_set (z3, p2->z);
}
else if (!mpi_cmp_ui (z2, 0))
{
mpi_set (x3, p1->x);
mpi_set (y3, p1->y);
mpi_set (z3, p1->z);
}
else
{
int z1_is_one = !mpi_cmp_ui (z1, 1);
int z2_is_one = !mpi_cmp_ui (z2, 1);
if (z2_is_one)
mpi_set (l1, x1);
else
{
ec_pow2 (l1, z2, ctx);
ec_mulm (l1, l1, x1, ctx);
}
if (z1_is_one)
mpi_set (l2, x2);
else
{
ec_pow2 (l2, z1, ctx);
ec_mulm (l2, l2, x2, ctx);
}
ec_subm (l3, l1, l2, ctx);
ec_powm (l4, z2, mpi_const (MPI_C_THREE), ctx);
ec_mulm (l4, l4, y1, ctx);
ec_powm (l5, z1, mpi_const (MPI_C_THREE), ctx);
ec_mulm (l5, l5, y2, ctx);
ec_subm (l6, l4, l5, ctx);
if (!mpi_cmp_ui (l3, 0))
{
if (!mpi_cmp_ui (l6, 0))
{
_gcry_mpi_ec_dup_point (result, p1, ctx);
}
else
{
mpi_set_ui (x3, 1);
mpi_set_ui (y3, 1);
mpi_set_ui (z3, 0);
}
}
else
{
ec_addm (l7, l1, l2, ctx);
ec_addm (l8, l4, l5, ctx);
ec_mulm (z3, z1, z2, ctx);
ec_mulm (z3, z3, l3, ctx);
ec_pow2 (t1, l6, ctx);
ec_pow2 (t2, l3, ctx);
ec_mulm (t2, t2, l7, ctx);
ec_subm (x3, t1, t2, ctx);
ec_mul2 (t1, x3, ctx);
ec_subm (l9, t2, t1, ctx);
ec_mulm (l9, l9, l6, ctx);
ec_powm (t1, l3, mpi_const (MPI_C_THREE), ctx);
ec_mulm (t1, t1, l8, ctx);
ec_subm (y3, l9, t1, ctx);
ec_mulm (y3, y3, ec_get_two_inv_p (ctx), ctx);
}
}
#undef x1
#undef y1
#undef z1
#undef x2
#undef y2
#undef z2
#undef x3
#undef y3
#undef z3
#undef l1
#undef l2
#undef l3
#undef l4
#undef l5
#undef l6
#undef l7
#undef l8
#undef l9
#undef t1
#undef t2
}
static void
add_points_montgomery (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
(void)result;
(void)p1;
(void)p2;
(void)ctx;
log_fatal ("%s: %s not yet supported\n",
"_gcry_mpi_ec_add_points", "Montgomery");
}
static void
add_points_edwards (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
#define X1 (p1->x)
#define Y1 (p1->y)
#define Z1 (p1->z)
#define X2 (p2->x)
#define Y2 (p2->y)
#define Z2 (p2->z)
#define X3 (result->x)
#define Y3 (result->y)
#define Z3 (result->z)
#define A (ctx->t.scratch[0])
#define B (ctx->t.scratch[1])
#define C (ctx->t.scratch[2])
#define D (ctx->t.scratch[3])
#define E (ctx->t.scratch[4])
#define F (ctx->t.scratch[5])
#define G (ctx->t.scratch[6])
#define tmp (ctx->t.scratch[7])
ec_mulm (A, Z1, Z2, ctx);
ec_pow2 (B, A, ctx);
ec_mulm (C, X1, X2, ctx);
ec_mulm (D, Y1, Y2, ctx);
ec_mulm (E, ctx->b, C, ctx);
ec_mulm (E, E, D, ctx);
ec_subm (F, B, E, ctx);
ec_addm (G, B, E, ctx);
ec_addm (tmp, X1, Y1, ctx);
ec_addm (X3, X2, Y2, ctx);
ec_mulm (X3, X3, tmp, ctx);
ec_subm (X3, X3, C, ctx);
ec_subm (X3, X3, D, ctx);
ec_mulm (X3, X3, F, ctx);
ec_mulm (X3, X3, A, ctx);
if (ctx->dialect == ECC_DIALECT_ED25519)
{
ec_addm (Y3, D, C, ctx);
}
else
{
ec_mulm (Y3, ctx->a, C, ctx);
ec_subm (Y3, D, Y3, ctx);
}
ec_mulm (Y3, Y3, G, ctx);
ec_mulm (Y3, Y3, A, ctx);
ec_mulm (Z3, F, G, ctx);
#undef X1
#undef Y1
#undef Z1
#undef X2
#undef Y2
#undef Z2
#undef X3
#undef Y3
#undef Z3
#undef A
#undef B
#undef C
#undef D
#undef E
#undef F
#undef G
#undef tmp
}
static void
montgomery_ladder (mpi_point_t prd, mpi_point_t sum,
mpi_point_t p1, mpi_point_t p2, gcry_mpi_t dif_x,
mpi_ec_t ctx)
{
ec_addm (sum->x, p2->x, p2->z, ctx);
ec_subm (p2->z, p2->x, p2->z, ctx);
ec_addm (prd->x, p1->x, p1->z, ctx);
ec_subm (p1->z, p1->x, p1->z, ctx);
ec_mulm (p2->x, p1->z, sum->x, ctx);
ec_mulm (p2->z, prd->x, p2->z, ctx);
ec_pow2 (p1->x, prd->x, ctx);
ec_pow2 (p1->z, p1->z, ctx);
ec_addm (sum->x, p2->x, p2->z, ctx);
ec_subm (p2->z, p2->x, p2->z, ctx);
ec_mulm (prd->x, p1->x, p1->z, ctx);
ec_subm (p1->z, p1->x, p1->z, ctx);
ec_pow2 (sum->x, sum->x, ctx);
ec_pow2 (sum->z, p2->z, ctx);
ec_mulm (prd->z, p1->z, ctx->a, ctx);
ec_mulm (sum->z, sum->z, dif_x, ctx);
ec_addm (prd->z, p1->x, prd->z, ctx);
ec_mulm (prd->z, prd->z, p1->z, ctx);
}
void
_gcry_mpi_ec_add_points (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
switch (ctx->model)
{
case MPI_EC_WEIERSTRASS:
add_points_weierstrass (result, p1, p2, ctx);
break;
case MPI_EC_MONTGOMERY:
add_points_montgomery (result, p1, p2, ctx);
break;
case MPI_EC_EDWARDS:
add_points_edwards (result, p1, p2, ctx);
break;
}
}
static void
sub_points_weierstrass (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
(void)result;
(void)p1;
(void)p2;
(void)ctx;
log_fatal ("%s: %s not yet supported\n",
"_gcry_mpi_ec_sub_points", "Weierstrass");
}
static void
sub_points_montgomery (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
(void)result;
(void)p1;
(void)p2;
(void)ctx;
log_fatal ("%s: %s not yet supported\n",
"_gcry_mpi_ec_sub_points", "Montgomery");
}
static void
sub_points_edwards (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
mpi_point_t p2i = _gcry_mpi_point_new (0);
point_set (p2i, p2);
mpi_sub (p2i->x, ctx->p, p2i->x);
add_points_edwards (result, p1, p2i, ctx);
_gcry_mpi_point_release (p2i);
}
void
_gcry_mpi_ec_sub_points (mpi_point_t result,
mpi_point_t p1, mpi_point_t p2,
mpi_ec_t ctx)
{
switch (ctx->model)
{
case MPI_EC_WEIERSTRASS:
sub_points_weierstrass (result, p1, p2, ctx);
break;
case MPI_EC_MONTGOMERY:
sub_points_montgomery (result, p1, p2, ctx);
break;
case MPI_EC_EDWARDS:
sub_points_edwards (result, p1, p2, ctx);
break;
}
}
void
_gcry_mpi_ec_mul_point (mpi_point_t result,
gcry_mpi_t scalar, mpi_point_t point,
mpi_ec_t ctx)
{
gcry_mpi_t x1, y1, z1, k, h, yy;
unsigned int i, loops;
mpi_point_struct p1, p2, p1inv;
if (ctx->model == MPI_EC_EDWARDS
|| (ctx->model == MPI_EC_WEIERSTRASS
&& mpi_is_secure (scalar)))
{
unsigned int nbits;
int j;
nbits = mpi_get_nbits (scalar);
if (ctx->model == MPI_EC_WEIERSTRASS)
{
mpi_set_ui (result->x, 1);
mpi_set_ui (result->y, 1);
mpi_set_ui (result->z, 0);
}
else
{
mpi_set_ui (result->x, 0);
mpi_set_ui (result->y, 1);
mpi_set_ui (result->z, 1);
}
if (mpi_is_secure (scalar))
{
mpi_point_struct tmppnt;
point_init (&tmppnt);
point_resize (result, ctx);
point_resize (&tmppnt, ctx);
for (j=nbits-1; j >= 0; j--)
{
_gcry_mpi_ec_dup_point (result, result, ctx);
_gcry_mpi_ec_add_points (&tmppnt, result, point, ctx);
point_swap_cond (result, &tmppnt, mpi_test_bit (scalar, j), ctx);
}
point_free (&tmppnt);
}
else
{
for (j=nbits-1; j >= 0; j--)
{
_gcry_mpi_ec_dup_point (result, result, ctx);
if (mpi_test_bit (scalar, j))
_gcry_mpi_ec_add_points (result, result, point, ctx);
}
}
return;
}
else if (ctx->model == MPI_EC_MONTGOMERY)
{
unsigned int nbits;
int j;
mpi_point_struct p1_, p2_;
mpi_point_t q1, q2, prd, sum;
unsigned long sw;
nbits = mpi_get_nbits (scalar);
point_init (&p1);
point_init (&p2);
point_init (&p1_);
point_init (&p2_);
mpi_set_ui (p1.x, 1);
mpi_free (p2.x);
p2.x = mpi_copy (point->x);
mpi_set_ui (p2.z, 1);
point_resize (&p1, ctx);
point_resize (&p2, ctx);
point_resize (&p1_, ctx);
point_resize (&p2_, ctx);
q1 = &p1;
q2 = &p2;
prd = &p1_;
sum = &p2_;
for (j=nbits-1; j >= 0; j--)
{
mpi_point_t t;
sw = mpi_test_bit (scalar, j);
point_swap_cond (q1, q2, sw, ctx);
montgomery_ladder (prd, sum, q1, q2, point->x, ctx);
point_swap_cond (prd, sum, sw, ctx);
t = q1; q1 = prd; prd = t;
t = q2; q2 = sum; sum = t;
}
mpi_clear (result->y);
sw = (nbits & 1);
point_swap_cond (&p1, &p1_, sw, ctx);
if (p1.z->nlimbs == 0)
{
mpi_set_ui (result->x, 1);
mpi_set_ui (result->z, 0);
}
else
{
z1 = mpi_new (0);
ec_invm (z1, p1.z, ctx);
ec_mulm (result->x, p1.x, z1, ctx);
mpi_set_ui (result->z, 1);
mpi_free (z1);
}
point_free (&p1);
point_free (&p2);
point_free (&p1_);
point_free (&p2_);
return;
}
x1 = mpi_alloc_like (ctx->p);
y1 = mpi_alloc_like (ctx->p);
h = mpi_alloc_like (ctx->p);
k = mpi_copy (scalar);
yy = mpi_copy (point->y);
if ( mpi_has_sign (k) )
{
k->sign = 0;
ec_invm (yy, yy, ctx);
}
if (!mpi_cmp_ui (point->z, 1))
{
mpi_set (x1, point->x);
mpi_set (y1, yy);
}
else
{
gcry_mpi_t z2, z3;
z2 = mpi_alloc_like (ctx->p);
z3 = mpi_alloc_like (ctx->p);
ec_mulm (z2, point->z, point->z, ctx);
ec_mulm (z3, point->z, z2, ctx);
ec_invm (z2, z2, ctx);
ec_mulm (x1, point->x, z2, ctx);
ec_invm (z3, z3, ctx);
ec_mulm (y1, yy, z3, ctx);
mpi_free (z2);
mpi_free (z3);
}
z1 = mpi_copy (mpi_const (MPI_C_ONE));
mpi_mul (h, k, mpi_const (MPI_C_THREE));
loops = mpi_get_nbits (h);
if (loops < 2)
{
loops = 2;
mpi_clear (result->x);
mpi_clear (result->y);
mpi_clear (result->z);
}
else
{
mpi_set (result->x, point->x);
mpi_set (result->y, yy);
mpi_set (result->z, point->z);
}
mpi_free (yy); yy = NULL;
p1.x = x1; x1 = NULL;
p1.y = y1; y1 = NULL;
p1.z = z1; z1 = NULL;
point_init (&p2);
point_init (&p1inv);
point_set (&p1inv, &p1);
ec_subm (p1inv.y, ctx->p, p1inv.y, ctx);
for (i=loops-2; i > 0; i--)
{
_gcry_mpi_ec_dup_point (result, result, ctx);
if (mpi_test_bit (h, i) == 1 && mpi_test_bit (k, i) == 0)
{
point_set (&p2, result);
_gcry_mpi_ec_add_points (result, &p2, &p1, ctx);
}
if (mpi_test_bit (h, i) == 0 && mpi_test_bit (k, i) == 1)
{
point_set (&p2, result);
_gcry_mpi_ec_add_points (result, &p2, &p1inv, ctx);
}
}
point_free (&p1);
point_free (&p2);
point_free (&p1inv);
mpi_free (h);
mpi_free (k);
}
int
_gcry_mpi_ec_curve_point (gcry_mpi_point_t point, mpi_ec_t ctx)
{
int res = 0;
gcry_mpi_t x, y, w;
x = mpi_new (0);
y = mpi_new (0);
w = mpi_new (0);
if (mpi_cmpabs (point->x, ctx->p) >= 0)
goto leave;
if (mpi_cmpabs (point->y, ctx->p) >= 0)
goto leave;
if (mpi_cmpabs (point->z, ctx->p) >= 0)
goto leave;
switch (ctx->model)
{
case MPI_EC_WEIERSTRASS:
{
gcry_mpi_t xxx;
if (_gcry_mpi_ec_get_affine (x, y, point, ctx))
goto leave;
xxx = mpi_new (0);
ec_pow2 (y, y, ctx);
ec_pow3 (xxx, x, ctx);
ec_mulm (w, ctx->a, x, ctx);
ec_addm (w, w, ctx->b, ctx);
ec_addm (w, w, xxx, ctx);
if (!mpi_cmp (y, w))
res = 1;
_gcry_mpi_release (xxx);
}
break;
case MPI_EC_MONTGOMERY:
{
#define xx y
if (_gcry_mpi_ec_get_affine (x, NULL, point, ctx))
goto leave;
ec_mulm (w, ctx->a, mpi_const (MPI_C_FOUR), ctx);
ec_addm (w, w, mpi_const (MPI_C_TWO), ctx);
ec_mulm (w, w, x, ctx);
ec_pow2 (xx, x, ctx);
ec_addm (w, w, xx, ctx);
ec_addm (w, w, mpi_const (MPI_C_ONE), ctx);
ec_mulm (w, w, x, ctx);
ec_mulm (w, w, ctx->b, ctx);
#undef xx
#define p_minus1 y
ec_subm (p_minus1, ctx->p, mpi_const (MPI_C_ONE), ctx);
mpi_rshift (p_minus1, p_minus1, 1);
ec_powm (w, w, p_minus1, ctx);
res = !mpi_cmp_ui (w, 1);
#undef p_minus1
}
break;
case MPI_EC_EDWARDS:
{
if (_gcry_mpi_ec_get_affine (x, y, point, ctx))
goto leave;
ec_pow2 (x, x, ctx);
ec_pow2 (y, y, ctx);
if (ctx->dialect == ECC_DIALECT_ED25519)
mpi_sub (w, ctx->p, x);
else
ec_mulm (w, ctx->a, x, ctx);
ec_addm (w, w, y, ctx);
ec_subm (w, w, mpi_const (MPI_C_ONE), ctx);
ec_mulm (x, x, y, ctx);
ec_mulm (x, x, ctx->b, ctx);
ec_subm (w, w, x, ctx);
if (!mpi_cmp_ui (w, 0))
res = 1;
}
break;
}
leave:
_gcry_mpi_release (w);
_gcry_mpi_release (x);
_gcry_mpi_release (y);
return res;
}
int
_gcry_mpi_ec_bad_point (gcry_mpi_point_t point, mpi_ec_t ctx)
{
int i;
gcry_mpi_t x_bad;
for (i = 0; (x_bad = ctx->t.scratch[i]); i++)
if (!mpi_cmp (point->x, x_bad))
return 1;
return 0;
}