/* Field multiplication using Montgomery reduction. */ mp_err ec_GFp_mul_mont(const mp_int *a, const mp_int *b, mp_int *r, const GFMethod *meth) { mp_err res = MP_OKAY; #ifdef MP_MONT_USE_MP_MUL /* if MP_MONT_USE_MP_MUL is defined, then the function s_mp_mul_mont * is not implemented and we have to use mp_mul and s_mp_redc directly */ MP_CHECKOK(mp_mul(a, b, r)); MP_CHECKOK(s_mp_redc(r, (mp_mont_modulus *) meth->extra1)); #else mp_int s; MP_DIGITS(&s) = 0; /* s_mp_mul_mont doesn't allow source and destination to be the same */ if ((a == r) || (b == r)) { MP_CHECKOK(mp_init(&s)); MP_CHECKOK(s_mp_mul_mont (a, b, &s, (mp_mont_modulus *) meth->extra1)); MP_CHECKOK(mp_copy(&s, r)); mp_clear(&s); } else { return s_mp_mul_mont(a, b, r, (mp_mont_modulus *) meth->extra1); } #endif CLEANUP: return res; }
/* Compute the x-coordinate x1/z1 for the point (x1/z1)+(x2/x2) in * Montgomery projective coordinates. Uses algorithm Madd in appendix of * Lopex, J. and Dahab, R. "Fast multiplication on elliptic curves over * GF(2^m) without precomputation". */ static mp_err gf2m_Madd(const mp_int *x, mp_int *x1, mp_int *z1, mp_int *x2, mp_int *z2, const ECGroup *group, int kmflag) { mp_err res = MP_OKAY; mp_int t1, t2; MP_DIGITS(&t1) = 0; MP_DIGITS(&t2) = 0; MP_CHECKOK(mp_init(&t1, kmflag)); MP_CHECKOK(mp_init(&t2, kmflag)); MP_CHECKOK(mp_copy(x, &t1)); MP_CHECKOK(group->meth->field_mul(x1, z2, x1, group->meth)); MP_CHECKOK(group->meth->field_mul(z1, x2, z1, group->meth)); MP_CHECKOK(group->meth->field_mul(x1, z1, &t2, group->meth)); MP_CHECKOK(group->meth->field_add(z1, x1, z1, group->meth)); MP_CHECKOK(group->meth->field_sqr(z1, z1, group->meth)); MP_CHECKOK(group->meth->field_mul(z1, &t1, x1, group->meth)); MP_CHECKOK(group->meth->field_add(x1, &t2, x1, group->meth)); CLEANUP: mp_clear(&t1); mp_clear(&t2); return res; }
/* Allocate memory for a new ECGroup object. */ ECGroup * ECGroup_new() { mp_err res = MP_OKAY; ECGroup *group; group = (ECGroup *) malloc(sizeof(ECGroup)); if (group == NULL) return NULL; group->constructed = MP_YES; group->meth = NULL; group->text = NULL; MP_DIGITS(&group->curvea) = 0; MP_DIGITS(&group->curveb) = 0; MP_DIGITS(&group->genx) = 0; MP_DIGITS(&group->geny) = 0; MP_DIGITS(&group->order) = 0; group->base_point_mul = NULL; group->points_mul = NULL; group->validate_point = NULL; group->extra1 = NULL; group->extra2 = NULL; group->extra_free = NULL; MP_CHECKOK(mp_init(&group->curvea)); MP_CHECKOK(mp_init(&group->curveb)); MP_CHECKOK(mp_init(&group->genx)); MP_CHECKOK(mp_init(&group->geny)); MP_CHECKOK(mp_init(&group->order)); CLEANUP: if (res != MP_OKAY) { ECGroup_free(group); return NULL; } return group; }
/* computes T = REDC(T), 2^b == R */ mp_err s_mp_redc(mp_int *T, mp_mont_modulus *mmm) { mp_err res; mp_size i; i = MP_USED(T) + MP_USED(&mmm->N) + 2; MP_CHECKOK( s_mp_pad(T, i) ); for (i = 0; i < MP_USED(&mmm->N); ++i ) { mp_digit m_i = MP_DIGIT(T, i) * mmm->n0prime; /* T += N * m_i * (MP_RADIX ** i); */ MP_CHECKOK( s_mp_mul_d_add_offset(&mmm->N, m_i, T, i) ); } s_mp_clamp(T); /* T /= R */ s_mp_div_2d(T, mmm->b); if ((res = s_mp_cmp(T, &mmm->N)) >= 0) { /* T = T - N */ MP_CHECKOK( s_mp_sub(T, &mmm->N) ); #ifdef DEBUG if ((res = mp_cmp(T, &mmm->N)) >= 0) { res = MP_UNDEF; goto CLEANUP; } #endif } res = MP_OKAY; CLEANUP: return res; }
/* Test point doubling in Jacobian coordinates */ mp_err testPointDoubleJac(ECGroup *ecgroup) { mp_err res; mp_int pz, rx, ry, rz, rx2, ry2, rz2; ecfp_jac_pt p, p2; EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; MP_DIGITS(&pz) = 0; MP_DIGITS(&rx) = 0; MP_DIGITS(&ry) = 0; MP_DIGITS(&rz) = 0; MP_DIGITS(&rx2) = 0; MP_DIGITS(&ry2) = 0; MP_DIGITS(&rz2) = 0; MP_CHECKOK(mp_init(&pz)); MP_CHECKOK(mp_init(&rx)); MP_CHECKOK(mp_init(&ry)); MP_CHECKOK(mp_init(&rz)); MP_CHECKOK(mp_init(&rx2)); MP_CHECKOK(mp_init(&ry2)); MP_CHECKOK(mp_init(&rz2)); MP_CHECKOK(mp_set_int(&pz, 5)); /* Set p2 = 2P */ ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); ecfp_i2fp(p.z, &pz, ecgroup); ecfp_i2fp(group->curvea, &ecgroup->curvea, ecgroup); group->pt_dbl_jac(&p, &p2, group); M_TimeOperation(group->pt_dbl_jac(&p, &p2, group), 100000); /* Calculate doubling to compare against */ ec_GFp_pt_dbl_jac(&ecgroup->genx, &ecgroup->geny, &pz, &rx2, &ry2, &rz2, ecgroup); ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); /* Do comparison */ MP_CHECKOK(testJacPoint(&p2, &rx2, &ry2, ecgroup)); CLEANUP: if (res == MP_OKAY) printf(" Test Passed - Point Doubling - Jacobian\n"); else printf("TEST FAILED - Point Doubling - Jacobian\n"); mp_clear(&pz); mp_clear(&rx); mp_clear(&ry); mp_clear(&rz); mp_clear(&rx2); mp_clear(&ry2); mp_clear(&rz2); return res; }
STATIC mp_err s_mp_to_mont(const mp_int *x, mp_mont_modulus *mmm, mp_int *xMont) { mp_err res; /* xMont = x * R mod N where N is modulus */ MP_CHECKOK( mpl_lsh(x, xMont, mmm->b) ); /* xMont = x << b */ MP_CHECKOK( mp_div(xMont, &mmm->N, 0, xMont) ); /* mod N */ CLEANUP: return res; }
/* Encode a field element in Montgomery form. See s_mp_to_mont in * mpi/mpmontg.c */ mp_err ec_GFp_enc_mont(const mp_int *a, mp_int *r, const GFMethod *meth) { mp_mont_modulus *mmm; mp_err res = MP_OKAY; mmm = (mp_mont_modulus *) meth->extra1; MP_CHECKOK(mpl_lsh(a, r, mmm->b)); MP_CHECKOK(mp_mod(r, &mmm->N, r)); CLEANUP: return res; }
/* Decode a field element from Montgomery form. */ mp_err ec_GFp_dec_mont(const mp_int *a, mp_int *r, const GFMethod *meth) { mp_err res = MP_OKAY; if (a != r) { MP_CHECKOK(mp_copy(a, r)); } MP_CHECKOK(s_mp_redc(r, (mp_mont_modulus *) meth->extra1)); CLEANUP: return res; }
/* Tests the time required for a point multiplication */ mp_err testPointMulTime(ECGroup *ecgroup) { mp_err res = MP_OKAY; mp_int rx, ry, n; int size; MP_DIGITS(&rx) = 0; MP_DIGITS(&ry) = 0; MP_DIGITS(&n) = 0; MP_CHECKOK(mp_init(&rx)); MP_CHECKOK(mp_init(&ry)); MP_CHECKOK(mp_init(&n)); /* compute random scalar */ size = mpl_significant_bits(&ecgroup->meth->irr); if (size < MP_OKAY) { res = MP_NO; goto CLEANUP; } MP_CHECKOK(mpp_random_size(&n, (size + ECL_BITS - 1) / ECL_BITS)); MP_CHECKOK(ecgroup->meth->field_mod(&n, &n, ecgroup->meth)); M_TimeOperation(ec_GFp_pt_mul_jac_fp (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, ecgroup), 1000); M_TimeOperation(ec_GFp_point_mul_jac_4w_fp (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, ecgroup), 1000); M_TimeOperation(ec_GFp_point_mul_wNAF_fp (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, ecgroup), 1000); M_TimeOperation(ec_GFp_pt_mul_jac (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, ecgroup), 100); CLEANUP: if (res == MP_OKAY) printf(" Test Passed - Point Multiplication Timing\n"); else printf("TEST FAILED - Point Multiplication Timing\n"); mp_clear(&rx); mp_clear(&ry); mp_clear(&n); return res; }
/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + * k2 * P(x, y), where G is the generator (base point) of the group of * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. * Input and output values are assumed to be NOT field-encoded. */ mp_err ECPoints_mul(const ECGroup *group, const mp_int *k1, const mp_int *k2, const mp_int *px, const mp_int *py, mp_int *rx, mp_int *ry) { mp_err res = MP_OKAY; mp_int k1t, k2t; const mp_int *k1p, *k2p; MP_DIGITS(&k1t) = 0; MP_DIGITS(&k2t) = 0; ARGCHK(group != NULL, MP_BADARG); /* want scalar to be less than or equal to group order */ if (k1 != NULL) { if (mp_cmp(k1, &group->order) >= 0) { MP_CHECKOK(mp_init(&k1t, FLAG(k1))); MP_CHECKOK(mp_mod(k1, &group->order, &k1t)); k1p = &k1t; } else { k1p = k1; } } else { k1p = k1; } if (k2 != NULL) { if (mp_cmp(k2, &group->order) >= 0) { MP_CHECKOK(mp_init(&k2t, FLAG(k2))); MP_CHECKOK(mp_mod(k2, &group->order, &k2t)); k2p = &k2t; } else { k2p = k2; } } else { k2p = k2; } /* if points_mul is defined, then use it */ if (group->points_mul) { res = group->points_mul(k1p, k2p, px, py, rx, ry, group); } else { res = ec_pts_mul_simul_w2(k1p, k2p, px, py, rx, ry, group); } CLEANUP: mp_clear(&k1t); mp_clear(&k2t); return res; }
/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k * P(x, * y). If x, y = NULL, then P is assumed to be the generator (base point) * of the group of points on the elliptic curve. Input and output values * are assumed to be NOT field-encoded. */ mp_err ECPoint_mul(const ECGroup *group, const mp_int *k, const mp_int *px, const mp_int *py, mp_int *rx, mp_int *ry) { mp_err res = MP_OKAY; mp_int kt; ARGCHK((k != NULL) && (group != NULL), MP_BADARG); MP_DIGITS(&kt) = 0; /* want scalar to be less than or equal to group order */ if (mp_cmp(k, &group->order) > 0) { MP_CHECKOK(mp_init(&kt, FLAG(k))); MP_CHECKOK(mp_mod(k, &group->order, &kt)); } else { MP_SIGN(&kt) = MP_ZPOS; MP_USED(&kt) = MP_USED(k); MP_ALLOC(&kt) = MP_ALLOC(k); MP_DIGITS(&kt) = MP_DIGITS(k); } if ((px == NULL) || (py == NULL)) { if (group->base_point_mul) { MP_CHECKOK(group->base_point_mul(&kt, rx, ry, group)); } else { MP_CHECKOK(group-> point_mul(&kt, &group->genx, &group->geny, rx, ry, group)); } } else { if (group->meth->field_enc) { MP_CHECKOK(group->meth->field_enc(px, rx, group->meth)); MP_CHECKOK(group->meth->field_enc(py, ry, group->meth)); MP_CHECKOK(group->point_mul(&kt, rx, ry, rx, ry, group)); } else { MP_CHECKOK(group->point_mul(&kt, px, py, rx, ry, group)); } } if (group->meth->field_dec) { MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); } CLEANUP: if (MP_DIGITS(&kt) != MP_DIGITS(k)) { mp_clear(&kt); } return res; }
/* Computes R = P - Q. Elliptic curve points P, Q, and R can all be * identical. Uses affine coordinates. Assumes input is already * field-encoded using field_enc, and returns output that is still * field-encoded. */ mp_err ec_GFp_pt_sub_aff(const mp_int *px, const mp_int *py, const mp_int *qx, const mp_int *qy, mp_int *rx, mp_int *ry, const ECGroup *group) { mp_err res = MP_OKAY; mp_int nqy; MP_DIGITS(&nqy) = 0; MP_CHECKOK(mp_init(&nqy)); /* nqy = -qy */ MP_CHECKOK(group->meth->field_neg(qy, &nqy, group->meth)); res = group->point_add(px, py, qx, &nqy, rx, ry, group); CLEANUP: mp_clear(&nqy); return res; }
/*! c <- REDC( a * b ) mod N \param a < N i.e. "reduced" \param b < N i.e. "reduced" \param mmm modulus N and n0' of N */ mp_err s_mp_mul_mont(const mp_int *a, const mp_int *b, mp_int *c, mp_mont_modulus *mmm) { mp_digit *pb; mp_digit m_i; mp_err res; mp_size ib; /* "index b": index of current digit of B */ mp_size useda, usedb; ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); if (MP_USED(a) < MP_USED(b)) { const mp_int *xch = b; /* switch a and b, to do fewer outer loops */ b = a; a = xch; } MP_USED(c) = 1; MP_DIGIT(c, 0) = 0; ib = (MP_USED(&mmm->N) << 1) + 1; if ((res = s_mp_pad(c, ib)) != MP_OKAY) goto CLEANUP; useda = MP_USED(a); pb = MP_DIGITS(b); s_mpv_mul_d(MP_DIGITS(a), useda, *pb++, MP_DIGITS(c)); s_mp_setz(MP_DIGITS(c) + useda + 1, ib - (useda + 1)); m_i = MP_DIGIT(c, 0) * mmm->n0prime; s_mp_mul_d_add_offset(&mmm->N, m_i, c, 0); /* Outer loop: Digits of b */ usedb = MP_USED(b); for (ib = 1; ib < usedb; ib++) { mp_digit b_i = *pb++; /* Inner product: Digits of a */ if (b_i) s_mpv_mul_d_add_prop(MP_DIGITS(a), useda, b_i, MP_DIGITS(c) + ib); m_i = MP_DIGIT(c, ib) * mmm->n0prime; s_mp_mul_d_add_offset(&mmm->N, m_i, c, ib); } if (usedb < MP_USED(&mmm->N)) { for (usedb = MP_USED(&mmm->N); ib < usedb; ++ib) { m_i = MP_DIGIT(c, ib) * mmm->n0prime; s_mp_mul_d_add_offset(&mmm->N, m_i, c, ib); } } s_mp_clamp(c); s_mp_rshd(c, MP_USED(&mmm->N)); /* c /= R */ if (s_mp_cmp(c, &mmm->N) >= 0) { MP_CHECKOK(s_mp_sub(c, &mmm->N)); } res = MP_OKAY; CLEANUP: return res; }
/* Field division using Montgomery reduction. */ mp_err ec_GFp_div_mont(const mp_int *a, const mp_int *b, mp_int *r, const GFMethod *meth) { mp_err res = MP_OKAY; /* if A=aZ represents a encoded in montgomery coordinates with Z and # * and \ respectively represent multiplication and division in * montgomery coordinates, then A\B = (a/b)Z = (A/B)Z and Binv = * (1/b)Z = (1/B)(Z^2) where B # Binv = Z */ MP_CHECKOK(ec_GFp_div(a, b, r, meth)); MP_CHECKOK(ec_GFp_enc_mont(r, r, meth)); if (a == NULL) { MP_CHECKOK(ec_GFp_enc_mont(r, r, meth)); } CLEANUP: return res; }
/* Tests point doubling in Chudnovsky Jacobian coordinates */ mp_err testPointDoubleChud(ECGroup *ecgroup) { mp_err res; mp_int px, py, pz, rx2, ry2, rz2; ecfp_aff_pt p; ecfp_chud_pt p2; EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; MP_DIGITS(&rx2) = 0; MP_DIGITS(&ry2) = 0; MP_DIGITS(&rz2) = 0; MP_DIGITS(&px) = 0; MP_DIGITS(&py) = 0; MP_DIGITS(&pz) = 0; MP_CHECKOK(mp_init(&rx2)); MP_CHECKOK(mp_init(&ry2)); MP_CHECKOK(mp_init(&rz2)); MP_CHECKOK(mp_init(&px)); MP_CHECKOK(mp_init(&py)); MP_CHECKOK(mp_init(&pz)); /* Set p2 = 2P */ ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); ecfp_i2fp(group->curvea, &ecgroup->curvea, ecgroup); group->pt_dbl_aff2chud(&p, &p2, group); /* Calculate doubling to compare against */ MP_CHECKOK(mp_set_int(&pz, 1)); ec_GFp_pt_dbl_jac(&ecgroup->genx, &ecgroup->geny, &pz, &rx2, &ry2, &rz2, ecgroup); ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); /* Do comparison and check az^4 */ MP_CHECKOK(testChudPoint(&p2, &rx2, &ry2, ecgroup)); CLEANUP: if (res == MP_OKAY) printf(" Test Passed - Point Doubling - Chudnovsky Jacobian\n"); else printf("TEST FAILED - Point Doubling - Chudnovsky Jacobian\n"); mp_clear(&rx2); mp_clear(&ry2); mp_clear(&rz2); mp_clear(&px); mp_clear(&py); mp_clear(&pz); return res; }
/* Converts a point P(px, py) from affine coordinates to Jacobian * projective coordinates R(rx, ry, rz). Assumes input is already * field-encoded using field_enc, and returns output that is still * field-encoded. */ mp_err ec_GFp_pt_aff2jac(const mp_int *px, const mp_int *py, mp_int *rx, mp_int *ry, mp_int *rz, const ECGroup *group) { mp_err res = MP_OKAY; if (ec_GFp_pt_is_inf_aff(px, py) == MP_YES) { MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); } else { MP_CHECKOK(mp_copy(px, rx)); MP_CHECKOK(mp_copy(py, ry)); MP_CHECKOK(mp_set_int(rz, 1)); if (group->meth->field_enc) { MP_CHECKOK(group->meth->field_enc(rz, rz, group->meth)); } } CLEANUP: return res; }
/* Construct a generic GFMethod for arithmetic over prime fields with * irreducible irr. */ GFMethod * GFMethod_consGFp(const mp_int *irr) { mp_err res = MP_OKAY; GFMethod *meth = NULL; meth = GFMethod_new(); if (meth == NULL) return NULL; MP_CHECKOK(mp_copy(irr, &meth->irr)); meth->irr_arr[0] = mpl_significant_bits(irr); meth->irr_arr[1] = meth->irr_arr[2] = meth->irr_arr[3] = meth->irr_arr[4] = 0; switch (MP_USED(&meth->irr)) { /* maybe we need 1 and 2 words here as well?*/ case 3: meth->field_add = &ec_GFp_add_3; meth->field_sub = &ec_GFp_sub_3; break; case 4: meth->field_add = &ec_GFp_add_4; meth->field_sub = &ec_GFp_sub_4; break; case 5: meth->field_add = &ec_GFp_add_5; meth->field_sub = &ec_GFp_sub_5; break; case 6: meth->field_add = &ec_GFp_add_6; meth->field_sub = &ec_GFp_sub_6; break; default: meth->field_add = &ec_GFp_add; meth->field_sub = &ec_GFp_sub; } meth->field_neg = &ec_GFp_neg; meth->field_mod = &ec_GFp_mod; meth->field_mul = &ec_GFp_mul; meth->field_sqr = &ec_GFp_sqr; meth->field_div = &ec_GFp_div; meth->field_enc = NULL; meth->field_dec = NULL; meth->extra1 = NULL; meth->extra2 = NULL; meth->extra_free = NULL; CLEANUP: if (res != MP_OKAY) { GFMethod_free(meth); return NULL; } return meth; }
/* Subtracts two field elements. Assumes that 0 <= a, b < meth->irr */ mp_err ec_GFp_sub(const mp_int *a, const mp_int *b, mp_int *r, const GFMethod *meth) { mp_err res = MP_OKAY; /* PRE: 0 <= a, b < p = meth->irr POST: 0 <= r < p, r = a - b (mod p) */ res = mp_sub(a, b, r); if (res == MP_RANGE) { MP_CHECKOK(mp_sub(b, a, r)); if (mp_cmp_z(r) < 0) { MP_CHECKOK(mp_add(r, &meth->irr, r)); } MP_CHECKOK(ec_GFp_neg(r, r, meth)); } if (mp_cmp_z(r) < 0) { MP_CHECKOK(mp_add(r, &meth->irr, r)); } CLEANUP: return res; }
/* Divides two field elements. If a is NULL, then returns the inverse of * b. */ mp_err ec_GFp_div(const mp_int *a, const mp_int *b, mp_int *r, const GFMethod *meth) { mp_err res = MP_OKAY; mp_int t; /* If a is NULL, then return the inverse of b, otherwise return a/b. */ if (a == NULL) { return mp_invmod(b, &meth->irr, r); } else { /* MPI doesn't support divmod, so we implement it using invmod and * mulmod. */ MP_CHECKOK(mp_init(&t)); MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); MP_CHECKOK(mp_mulmod(a, &t, &meth->irr, r)); CLEANUP: mp_clear(&t); return res; } }
/* Divides two field elements. If a is NULL, then returns the inverse of * b. */ mp_err ec_GF2m_div(const mp_int *a, const mp_int *b, mp_int *r, const GFMethod *meth) { mp_err res = MP_OKAY; mp_int t; /* If a is NULL, then return the inverse of b, otherwise return a/b. */ if (a == NULL) { /* The GF(2^m) portion of MPI doesn't support invmod, so we * compute 1/b. */ MP_CHECKOK(mp_init(&t)); MP_CHECKOK(mp_set_int(&t, 1)); MP_CHECKOK(mp_bdivmod(&t, b, &meth->irr, meth->irr_arr, r)); CLEANUP: mp_clear(&t); return res; } else { return mp_bdivmod(a, b, &meth->irr, meth->irr_arr, r); } }
/* Construct a generic ECGroup for elliptic curves over prime fields with * field arithmetic implemented in Montgomery coordinates. */ ECGroup * ECGroup_consGFp_mont(const mp_int *irr, const mp_int *curvea, const mp_int *curveb, const mp_int *genx, const mp_int *geny, const mp_int *order, int cofactor) { mp_err res = MP_OKAY; ECGroup *group = NULL; group = ECGroup_new(); if (group == NULL) return NULL; group->meth = GFMethod_consGFp_mont(irr); if (group->meth == NULL) { res = MP_MEM; goto CLEANUP; } MP_CHECKOK(group->meth-> field_enc(curvea, &group->curvea, group->meth)); MP_CHECKOK(group->meth-> field_enc(curveb, &group->curveb, group->meth)); MP_CHECKOK(group->meth->field_enc(genx, &group->genx, group->meth)); MP_CHECKOK(group->meth->field_enc(geny, &group->geny, group->meth)); MP_CHECKOK(mp_copy(order, &group->order)); group->cofactor = cofactor; group->point_add = &ec_GFp_pt_add_aff; group->point_sub = &ec_GFp_pt_sub_aff; group->point_dbl = &ec_GFp_pt_dbl_aff; group->point_mul = &ec_GFp_pt_mul_jm_wNAF; group->base_point_mul = NULL; group->points_mul = &ec_GFp_pts_mul_jac; group->validate_point = &ec_GFp_validate_point; CLEANUP: if (res != MP_OKAY) { ECGroup_free(group); return NULL; } return group; }
/* Tests point addition of Jacobian + Affine -> Jacobian */ mp_err testPointAddJacAff(ECGroup *ecgroup) { mp_err res; mp_int pz, rx2, ry2, rz2; ecfp_jac_pt p, r; ecfp_aff_pt q; EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; /* Init */ MP_DIGITS(&pz) = 0; MP_DIGITS(&rx2) = 0; MP_DIGITS(&ry2) = 0; MP_DIGITS(&rz2) = 0; MP_CHECKOK(mp_init(&pz)); MP_CHECKOK(mp_init(&rx2)); MP_CHECKOK(mp_init(&ry2)); MP_CHECKOK(mp_init(&rz2)); MP_CHECKOK(mp_set_int(&pz, 5)); /* Set p */ ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); ecfp_i2fp(p.z, &pz, ecgroup); /* Set q */ ecfp_i2fp(q.x, &ecgroup->geny, ecgroup); ecfp_i2fp(q.y, &ecgroup->genx, ecgroup); /* Do calculations */ group->pt_add_jac_aff(&p, &q, &r, group); /* Do calculation in integer to compare against */ MP_CHECKOK(ec_GFp_pt_add_jac_aff (&ecgroup->genx, &ecgroup->geny, &pz, &ecgroup->geny, &ecgroup->genx, &rx2, &ry2, &rz2, ecgroup)); /* convert result R to affine coordinates */ ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); MP_CHECKOK(testJacPoint(&r, &rx2, &ry2, ecgroup)); CLEANUP: if (res == MP_OKAY) printf(" Test Passed - Point Addition - Jacobian & Affine\n"); else printf("TEST FAILED - Point Addition - Jacobian & Affine\n"); mp_clear(&pz); mp_clear(&rx2); mp_clear(&ry2); mp_clear(&rz2); return res; }
/* Construct a generic ECGroup for elliptic curves over binary polynomial * fields. */ ECGroup * ECGroup_consGF2m(const mp_int *irr, const unsigned int irr_arr[5], const mp_int *curvea, const mp_int *curveb, const mp_int *genx, const mp_int *geny, const mp_int *order, int cofactor) { mp_err res = MP_OKAY; ECGroup *group = NULL; group = ECGroup_new(); if (group == NULL) return NULL; group->meth = GFMethod_consGF2m(irr, irr_arr); if (group->meth == NULL) { res = MP_MEM; goto CLEANUP; } MP_CHECKOK(mp_copy(curvea, &group->curvea)); MP_CHECKOK(mp_copy(curveb, &group->curveb)); MP_CHECKOK(mp_copy(genx, &group->genx)); MP_CHECKOK(mp_copy(geny, &group->geny)); MP_CHECKOK(mp_copy(order, &group->order)); group->cofactor = cofactor; group->point_add = &ec_GF2m_pt_add_aff; group->point_sub = &ec_GF2m_pt_sub_aff; group->point_dbl = &ec_GF2m_pt_dbl_aff; group->point_mul = &ec_GF2m_pt_mul_mont; group->base_point_mul = NULL; group->points_mul = &ec_pts_mul_basic; group->validate_point = &ec_GF2m_validate_point; CLEANUP: if (res != MP_OKAY) { ECGroup_free(group); return NULL; } return group; }
/* Allocate memory for a new ECGroup object. */ ECGroup * ECGroup_new(int kmflag) { mp_err res = MP_OKAY; ECGroup *group; #ifdef _KERNEL group = (ECGroup *) kmem_alloc(sizeof(ECGroup), kmflag); #else group = (ECGroup *) malloc(sizeof(ECGroup)); #endif if (group == NULL) return NULL; group->constructed = MP_YES; group->meth = NULL; group->text = NULL; MP_DIGITS(&group->curvea) = 0; MP_DIGITS(&group->curveb) = 0; MP_DIGITS(&group->genx) = 0; MP_DIGITS(&group->geny) = 0; MP_DIGITS(&group->order) = 0; group->base_point_mul = NULL; group->points_mul = NULL; group->validate_point = NULL; group->extra1 = NULL; group->extra2 = NULL; group->extra_free = NULL; MP_CHECKOK(mp_init(&group->curvea, kmflag)); MP_CHECKOK(mp_init(&group->curveb, kmflag)); MP_CHECKOK(mp_init(&group->genx, kmflag)); MP_CHECKOK(mp_init(&group->geny, kmflag)); MP_CHECKOK(mp_init(&group->order, kmflag)); CLEANUP: if (res != MP_OKAY) { ECGroup_free(group); return NULL; } return group; }
/* Computes the windowed non-adjacent-form (NAF) of a scalar. Out should * be an array of signed char's to output to, bitsize should be the number * of bits of out, in is the original scalar, and w is the window size. * NAF is discussed in the paper: D. Hankerson, J. Hernandez and A. * Menezes, "Software implementation of elliptic curve cryptography over * binary fields", Proc. CHES 2000. */ mp_err ec_compute_wNAF(signed char *out, int bitsize, const mp_int *in, int w) { mp_int k; mp_err res = MP_OKAY; int i, twowm1, mask; twowm1 = ec_twoTo(w - 1); mask = 2 * twowm1 - 1; MP_DIGITS(&k) = 0; MP_CHECKOK(mp_init_copy(&k, in)); i = 0; /* Compute wNAF form */ while (mp_cmp_z(&k) > 0) { if (mp_isodd(&k)) { out[i] = MP_DIGIT(&k, 0) & mask; if (out[i] >= twowm1) out[i] -= 2 * twowm1; /* Subtract off out[i]. Note mp_sub_d only works with * unsigned digits */ if (out[i] >= 0) { mp_sub_d(&k, out[i], &k); } else { mp_add_d(&k, -(out[i]), &k); } } else { out[i] = 0; } mp_div_2(&k, &k); i++; } /* Zero out the remaining elements of the out array. */ for (; i < bitsize + 1; i++) { out[i] = 0; } CLEANUP: mp_clear(&k); return res; }
/* Compute the x-coordinate x/z for the point 2*(x/z) in Montgomery * projective coordinates. Uses algorithm Mdouble in appendix of Lopez, J. * and Dahab, R. "Fast multiplication on elliptic curves over GF(2^m) * without precomputation". modified to not require precomputation of * c=b^{2^{m-1}}. */ static mp_err gf2m_Mdouble(mp_int *x, mp_int *z, const ECGroup *group, int kmflag) { mp_err res = MP_OKAY; mp_int t1; MP_DIGITS(&t1) = 0; MP_CHECKOK(mp_init(&t1, kmflag)); MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); MP_CHECKOK(group->meth->field_sqr(z, &t1, group->meth)); MP_CHECKOK(group->meth->field_mul(x, &t1, z, group->meth)); MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); MP_CHECKOK(group->meth->field_sqr(&t1, &t1, group->meth)); MP_CHECKOK(group->meth-> field_mul(&group->curveb, &t1, &t1, group->meth)); MP_CHECKOK(group->meth->field_add(x, &t1, x, group->meth)); CLEANUP: mp_clear(&t1); return res; }
/* Allocate memory for a new GFMethod object. */ GFMethod * GFMethod_new() { mp_err res = MP_OKAY; GFMethod *meth; meth = (GFMethod *)malloc(sizeof(GFMethod)); if (meth == NULL) return NULL; meth->constructed = MP_YES; MP_DIGITS(&meth->irr) = 0; meth->extra_free = NULL; MP_CHECKOK(mp_init(&meth->irr)); CLEANUP: if (res != MP_OKAY) { GFMethod_free(meth); return NULL; } return meth; }
/* Tests a point p in Jacobian coordinates, comparing against the * expected affine result (x, y). */ mp_err testJacPoint(ecfp_jac_pt * p, mp_int *x, mp_int *y, ECGroup *ecgroup) { char s[1000]; mp_int rx, ry, rz; mp_err res = MP_OKAY; MP_DIGITS(&rx) = 0; MP_DIGITS(&ry) = 0; MP_DIGITS(&rz) = 0; MP_CHECKOK(mp_init(&rx)); MP_CHECKOK(mp_init(&ry)); MP_CHECKOK(mp_init(&rz)); ecfp_fp2i(&rx, p->x, ecgroup); ecfp_fp2i(&ry, p->y, ecgroup); ecfp_fp2i(&rz, p->z, ecgroup); /* convert result R to affine coordinates */ ec_GFp_pt_jac2aff(&rx, &ry, &rz, &rx, &ry, ecgroup); /* Compare to expected result */ if ((mp_cmp(&rx, x) != 0) || (mp_cmp(&ry, y) != 0)) { printf(" Error: Jacobian Floating Point Incorrect.\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf("floating point result\nrx %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf("ry %s\n", s); MP_CHECKOK(mp_toradix(x, s, 16)); printf("integer result\nx %s\n", s); MP_CHECKOK(mp_toradix(y, s, 16)); printf("y %s\n", s); res = MP_NO; goto CLEANUP; } CLEANUP: mp_clear(&rx); mp_clear(&ry); mp_clear(&rz); return res; }
/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses * Modified Jacobian coordinates. * * Assumes input is already field-encoded using field_enc, and returns * output that is still field-encoded. * */ mp_err ec_GFp_pt_dbl_jm(const mp_int *px, const mp_int *py, const mp_int *pz, const mp_int *paz4, mp_int *rx, mp_int *ry, mp_int *rz, mp_int *raz4, mp_int scratch[], const ECGroup *group) { mp_err res = MP_OKAY; mp_int *t0, *t1, *M, *S; t0 = &scratch[0]; t1 = &scratch[1]; M = &scratch[2]; S = &scratch[3]; #if MAX_SCRATCH < 4 #error "Scratch array defined too small " #endif /* Check for point at infinity */ if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { /* Set r = pt at infinity by setting rz = 0 */ MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); goto CLEANUP; } /* M = 3 (px^2) + a*(pz^4) */ MP_CHECKOK(group->meth->field_sqr(px, t0, group->meth)); MP_CHECKOK(group->meth->field_add(t0, t0, M, group->meth)); MP_CHECKOK(group->meth->field_add(t0, M, t0, group->meth)); MP_CHECKOK(group->meth->field_add(t0, paz4, M, group->meth)); /* rz = 2 * py * pz */ MP_CHECKOK(group->meth->field_mul(py, pz, S, group->meth)); MP_CHECKOK(group->meth->field_add(S, S, rz, group->meth)); /* t0 = 2y^2 , t1 = 8y^4 */ MP_CHECKOK(group->meth->field_sqr(py, t0, group->meth)); MP_CHECKOK(group->meth->field_add(t0, t0, t0, group->meth)); MP_CHECKOK(group->meth->field_sqr(t0, t1, group->meth)); MP_CHECKOK(group->meth->field_add(t1, t1, t1, group->meth)); /* S = 4 * px * py^2 = 2 * px * t0 */ MP_CHECKOK(group->meth->field_mul(px, t0, S, group->meth)); MP_CHECKOK(group->meth->field_add(S, S, S, group->meth)); /* rx = M^2 - 2S */ MP_CHECKOK(group->meth->field_sqr(M, rx, group->meth)); MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); /* ry = M * (S - rx) - t1 */ MP_CHECKOK(group->meth->field_sub(S, rx, S, group->meth)); MP_CHECKOK(group->meth->field_mul(S, M, ry, group->meth)); MP_CHECKOK(group->meth->field_sub(ry, t1, ry, group->meth)); /* ra*z^4 = 2*t1*(apz4) */ MP_CHECKOK(group->meth->field_mul(paz4, t1, raz4, group->meth)); MP_CHECKOK(group->meth->field_add(raz4, raz4, raz4, group->meth)); CLEANUP: return res; }
/* Computes R = nP where R is (rx, ry) and P is the base point. Elliptic * curve points P and R can be identical. Uses mixed Modified-Jacobian * co-ordinates for doubling and Chudnovsky Jacobian coordinates for * additions. Assumes input is already field-encoded using field_enc, and * returns output that is still field-encoded. Uses 5-bit window NAF * method (algorithm 11) for scalar-point multiplication from Brown, * Hankerson, Lopez, Menezes. Software Implementation of the NIST Elliptic * Curves Over Prime Fields. */ mp_err ec_GFp_pt_mul_jm_wNAF(const mp_int *n, const mp_int *px, const mp_int *py, mp_int *rx, mp_int *ry, const ECGroup *group) { mp_err res = MP_OKAY; mp_int precomp[16][2], rz, tpx, tpy; mp_int raz4; mp_int scratch[MAX_SCRATCH]; signed char *naf = NULL; int i, orderBitSize; MP_DIGITS(&rz) = 0; MP_DIGITS(&raz4) = 0; MP_DIGITS(&tpx) = 0; MP_DIGITS(&tpy) = 0; for (i = 0; i < 16; i++) { MP_DIGITS(&precomp[i][0]) = 0; MP_DIGITS(&precomp[i][1]) = 0; } for (i = 0; i < MAX_SCRATCH; i++) { MP_DIGITS(&scratch[i]) = 0; } ARGCHK(group != NULL, MP_BADARG); ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); /* initialize precomputation table */ MP_CHECKOK(mp_init(&tpx, FLAG(n))); MP_CHECKOK(mp_init(&tpy, FLAG(n)));; MP_CHECKOK(mp_init(&rz, FLAG(n))); MP_CHECKOK(mp_init(&raz4, FLAG(n))); for (i = 0; i < 16; i++) { MP_CHECKOK(mp_init(&precomp[i][0], FLAG(n))); MP_CHECKOK(mp_init(&precomp[i][1], FLAG(n))); } for (i = 0; i < MAX_SCRATCH; i++) { MP_CHECKOK(mp_init(&scratch[i], FLAG(n))); } /* Set out[8] = P */ MP_CHECKOK(mp_copy(px, &precomp[8][0])); MP_CHECKOK(mp_copy(py, &precomp[8][1])); /* Set (tpx, tpy) = 2P */ MP_CHECKOK(group-> point_dbl(&precomp[8][0], &precomp[8][1], &tpx, &tpy, group)); /* Set 3P, 5P, ..., 15P */ for (i = 8; i < 15; i++) { MP_CHECKOK(group-> point_add(&precomp[i][0], &precomp[i][1], &tpx, &tpy, &precomp[i + 1][0], &precomp[i + 1][1], group)); } /* Set -15P, -13P, ..., -P */ for (i = 0; i < 8; i++) { MP_CHECKOK(mp_copy(&precomp[15 - i][0], &precomp[i][0])); MP_CHECKOK(group->meth-> field_neg(&precomp[15 - i][1], &precomp[i][1], group->meth)); } /* R = inf */ MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); orderBitSize = mpl_significant_bits(&group->order); /* Allocate memory for NAF */ #ifdef _KERNEL naf = (signed char *) kmem_alloc((orderBitSize + 1), FLAG(n)); #else naf = (signed char *) malloc(sizeof(signed char) * (orderBitSize + 1)); if (naf == NULL) { res = MP_MEM; goto CLEANUP; } #endif /* Compute 5NAF */ ec_compute_wNAF(naf, orderBitSize, n, 5); /* wNAF method */ for (i = orderBitSize; i >= 0; i--) { /* R = 2R */ ec_GFp_pt_dbl_jm(rx, ry, &rz, &raz4, rx, ry, &rz, &raz4, scratch, group); if (naf[i] != 0) { ec_GFp_pt_add_jm_aff(rx, ry, &rz, &raz4, &precomp[(naf[i] + 15) / 2][0], &precomp[(naf[i] + 15) / 2][1], rx, ry, &rz, &raz4, scratch, group); } } /* convert result S to affine coordinates */ MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); CLEANUP: for (i = 0; i < MAX_SCRATCH; i++) { mp_clear(&scratch[i]); } for (i = 0; i < 16; i++) { mp_clear(&precomp[i][0]); mp_clear(&precomp[i][1]); } mp_clear(&tpx); mp_clear(&tpy); mp_clear(&rz); mp_clear(&raz4); #ifdef _KERNEL kmem_free(naf, (orderBitSize + 1)); #else free(naf); #endif return res; }