michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ec2.h" michael@0: #include "mplogic.h" michael@0: #include "mp_gf2m.h" michael@0: #include michael@0: michael@0: /* Compute the x-coordinate x/z for the point 2*(x/z) in Montgomery michael@0: * projective coordinates. Uses algorithm Mdouble in appendix of Lopez, J. michael@0: * and Dahab, R. "Fast multiplication on elliptic curves over GF(2^m) michael@0: * without precomputation". modified to not require precomputation of michael@0: * c=b^{2^{m-1}}. */ michael@0: static mp_err michael@0: gf2m_Mdouble(mp_int *x, mp_int *z, const ECGroup *group) michael@0: { michael@0: mp_err res = MP_OKAY; michael@0: mp_int t1; michael@0: michael@0: MP_DIGITS(&t1) = 0; michael@0: MP_CHECKOK(mp_init(&t1)); michael@0: michael@0: MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); michael@0: MP_CHECKOK(group->meth->field_sqr(z, &t1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(x, &t1, z, group->meth)); michael@0: MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); michael@0: MP_CHECKOK(group->meth->field_sqr(&t1, &t1, group->meth)); michael@0: MP_CHECKOK(group->meth-> michael@0: field_mul(&group->curveb, &t1, &t1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(x, &t1, x, group->meth)); michael@0: michael@0: CLEANUP: michael@0: mp_clear(&t1); michael@0: return res; michael@0: } michael@0: michael@0: /* Compute the x-coordinate x1/z1 for the point (x1/z1)+(x2/x2) in michael@0: * Montgomery projective coordinates. Uses algorithm Madd in appendix of michael@0: * Lopex, J. and Dahab, R. "Fast multiplication on elliptic curves over michael@0: * GF(2^m) without precomputation". */ michael@0: static mp_err michael@0: gf2m_Madd(const mp_int *x, mp_int *x1, mp_int *z1, mp_int *x2, mp_int *z2, michael@0: const ECGroup *group) michael@0: { michael@0: mp_err res = MP_OKAY; michael@0: mp_int t1, t2; michael@0: michael@0: MP_DIGITS(&t1) = 0; michael@0: MP_DIGITS(&t2) = 0; michael@0: MP_CHECKOK(mp_init(&t1)); michael@0: MP_CHECKOK(mp_init(&t2)); michael@0: michael@0: MP_CHECKOK(mp_copy(x, &t1)); michael@0: MP_CHECKOK(group->meth->field_mul(x1, z2, x1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(z1, x2, z1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(x1, z1, &t2, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(z1, x1, z1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_sqr(z1, z1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(z1, &t1, x1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(x1, &t2, x1, group->meth)); michael@0: michael@0: CLEANUP: michael@0: mp_clear(&t1); michael@0: mp_clear(&t2); michael@0: return res; michael@0: } michael@0: michael@0: /* Compute the x, y affine coordinates from the point (x1, z1) (x2, z2) michael@0: * using Montgomery point multiplication algorithm Mxy() in appendix of michael@0: * Lopex, J. and Dahab, R. "Fast multiplication on elliptic curves over michael@0: * GF(2^m) without precomputation". Returns: 0 on error 1 if return value michael@0: * should be the point at infinity 2 otherwise */ michael@0: static int michael@0: gf2m_Mxy(const mp_int *x, const mp_int *y, mp_int *x1, mp_int *z1, michael@0: mp_int *x2, mp_int *z2, const ECGroup *group) michael@0: { michael@0: mp_err res = MP_OKAY; michael@0: int ret = 0; michael@0: mp_int t3, t4, t5; michael@0: michael@0: MP_DIGITS(&t3) = 0; michael@0: MP_DIGITS(&t4) = 0; michael@0: MP_DIGITS(&t5) = 0; michael@0: MP_CHECKOK(mp_init(&t3)); michael@0: MP_CHECKOK(mp_init(&t4)); michael@0: MP_CHECKOK(mp_init(&t5)); michael@0: michael@0: if (mp_cmp_z(z1) == 0) { michael@0: mp_zero(x2); michael@0: mp_zero(z2); michael@0: ret = 1; michael@0: goto CLEANUP; michael@0: } michael@0: michael@0: if (mp_cmp_z(z2) == 0) { michael@0: MP_CHECKOK(mp_copy(x, x2)); michael@0: MP_CHECKOK(group->meth->field_add(x, y, z2, group->meth)); michael@0: ret = 2; michael@0: goto CLEANUP; michael@0: } michael@0: michael@0: MP_CHECKOK(mp_set_int(&t5, 1)); michael@0: if (group->meth->field_enc) { michael@0: MP_CHECKOK(group->meth->field_enc(&t5, &t5, group->meth)); michael@0: } michael@0: michael@0: MP_CHECKOK(group->meth->field_mul(z1, z2, &t3, group->meth)); michael@0: michael@0: MP_CHECKOK(group->meth->field_mul(z1, x, z1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(z1, x1, z1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(z2, x, z2, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(z2, x1, x1, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(z2, x2, z2, group->meth)); michael@0: michael@0: MP_CHECKOK(group->meth->field_mul(z2, z1, z2, group->meth)); michael@0: MP_CHECKOK(group->meth->field_sqr(x, &t4, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(&t4, y, &t4, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(&t4, &t3, &t4, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(&t4, z2, &t4, group->meth)); michael@0: michael@0: MP_CHECKOK(group->meth->field_mul(&t3, x, &t3, group->meth)); michael@0: MP_CHECKOK(group->meth->field_div(&t5, &t3, &t3, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(&t3, &t4, &t4, group->meth)); michael@0: MP_CHECKOK(group->meth->field_mul(x1, &t3, x2, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(x2, x, z2, group->meth)); michael@0: michael@0: MP_CHECKOK(group->meth->field_mul(z2, &t4, z2, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(z2, y, z2, group->meth)); michael@0: michael@0: ret = 2; michael@0: michael@0: CLEANUP: michael@0: mp_clear(&t3); michael@0: mp_clear(&t4); michael@0: mp_clear(&t5); michael@0: if (res == MP_OKAY) { michael@0: return ret; michael@0: } else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: /* Computes R = nP based on algorithm 2P of Lopex, J. and Dahab, R. "Fast michael@0: * multiplication on elliptic curves over GF(2^m) without michael@0: * precomputation". Elliptic curve points P and R can be identical. Uses michael@0: * Montgomery projective coordinates. */ michael@0: mp_err michael@0: ec_GF2m_pt_mul_mont(const mp_int *n, const mp_int *px, const mp_int *py, michael@0: mp_int *rx, mp_int *ry, const ECGroup *group) michael@0: { michael@0: mp_err res = MP_OKAY; michael@0: mp_int x1, x2, z1, z2; michael@0: int i, j; michael@0: mp_digit top_bit, mask; michael@0: michael@0: MP_DIGITS(&x1) = 0; michael@0: MP_DIGITS(&x2) = 0; michael@0: MP_DIGITS(&z1) = 0; michael@0: MP_DIGITS(&z2) = 0; michael@0: MP_CHECKOK(mp_init(&x1)); michael@0: MP_CHECKOK(mp_init(&x2)); michael@0: MP_CHECKOK(mp_init(&z1)); michael@0: MP_CHECKOK(mp_init(&z2)); michael@0: michael@0: /* if result should be point at infinity */ michael@0: if ((mp_cmp_z(n) == 0) || (ec_GF2m_pt_is_inf_aff(px, py) == MP_YES)) { michael@0: MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); michael@0: goto CLEANUP; michael@0: } michael@0: michael@0: MP_CHECKOK(mp_copy(px, &x1)); /* x1 = px */ michael@0: MP_CHECKOK(mp_set_int(&z1, 1)); /* z1 = 1 */ michael@0: MP_CHECKOK(group->meth->field_sqr(&x1, &z2, group->meth)); /* z2 = michael@0: * x1^2 = michael@0: * px^2 */ michael@0: MP_CHECKOK(group->meth->field_sqr(&z2, &x2, group->meth)); michael@0: MP_CHECKOK(group->meth->field_add(&x2, &group->curveb, &x2, group->meth)); /* x2 michael@0: * = michael@0: * px^4 michael@0: * + michael@0: * b michael@0: */ michael@0: michael@0: /* find top-most bit and go one past it */ michael@0: i = MP_USED(n) - 1; michael@0: j = MP_DIGIT_BIT - 1; michael@0: top_bit = 1; michael@0: top_bit <<= MP_DIGIT_BIT - 1; michael@0: mask = top_bit; michael@0: while (!(MP_DIGITS(n)[i] & mask)) { michael@0: mask >>= 1; michael@0: j--; michael@0: } michael@0: mask >>= 1; michael@0: j--; michael@0: michael@0: /* if top most bit was at word break, go to next word */ michael@0: if (!mask) { michael@0: i--; michael@0: j = MP_DIGIT_BIT - 1; michael@0: mask = top_bit; michael@0: } michael@0: michael@0: for (; i >= 0; i--) { michael@0: for (; j >= 0; j--) { michael@0: if (MP_DIGITS(n)[i] & mask) { michael@0: MP_CHECKOK(gf2m_Madd(px, &x1, &z1, &x2, &z2, group)); michael@0: MP_CHECKOK(gf2m_Mdouble(&x2, &z2, group)); michael@0: } else { michael@0: MP_CHECKOK(gf2m_Madd(px, &x2, &z2, &x1, &z1, group)); michael@0: MP_CHECKOK(gf2m_Mdouble(&x1, &z1, group)); michael@0: } michael@0: mask >>= 1; michael@0: } michael@0: j = MP_DIGIT_BIT - 1; michael@0: mask = top_bit; michael@0: } michael@0: michael@0: /* convert out of "projective" coordinates */ michael@0: i = gf2m_Mxy(px, py, &x1, &z1, &x2, &z2, group); michael@0: if (i == 0) { michael@0: res = MP_BADARG; michael@0: goto CLEANUP; michael@0: } else if (i == 1) { michael@0: MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); michael@0: } else { michael@0: MP_CHECKOK(mp_copy(&x2, rx)); michael@0: MP_CHECKOK(mp_copy(&z2, ry)); michael@0: } michael@0: michael@0: CLEANUP: michael@0: mp_clear(&x1); michael@0: mp_clear(&x2); michael@0: mp_clear(&z1); michael@0: mp_clear(&z2); michael@0: return res; michael@0: }