/* 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 ec_pts_mul_basic(const mp_int *k1, const mp_int *k2, const mp_int *px, const mp_int *py, mp_int *rx, mp_int *ry, const ECGroup *group) { mp_err res = MP_OKAY; mp_int sx, sy; ARGCHK(group != NULL, MP_BADARG); ARGCHK(!((k1 == NULL) && ((k2 == NULL) || (px == NULL) || (py == NULL))), MP_BADARG); /* if some arguments are not defined used ECPoint_mul */ if (k1 == NULL) { return ECPoint_mul(group, k2, px, py, rx, ry); } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { return ECPoint_mul(group, k1, NULL, NULL, rx, ry); } MP_DIGITS(&sx) = 0; MP_DIGITS(&sy) = 0; MP_CHECKOK(mp_init(&sx, FLAG(k1))); MP_CHECKOK(mp_init(&sy, FLAG(k1))); MP_CHECKOK(ECPoint_mul(group, k1, NULL, NULL, &sx, &sy)); MP_CHECKOK(ECPoint_mul(group, k2, px, py, rx, ry)); if (group->meth->field_enc) { MP_CHECKOK(group->meth->field_enc(&sx, &sx, group->meth)); MP_CHECKOK(group->meth->field_enc(&sy, &sy, group->meth)); MP_CHECKOK(group->meth->field_enc(rx, rx, group->meth)); MP_CHECKOK(group->meth->field_enc(ry, ry, group->meth)); } MP_CHECKOK(group->point_add(&sx, &sy, rx, ry, 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: mp_clear(&sx); mp_clear(&sy); 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. * Uses mixed Jacobian-affine coordinates. Input and output values are * assumed to be NOT field-encoded. Uses algorithm 15 (simultaneous * multiple point multiplication) from Brown, Hankerson, Lopez, Menezes. * Software Implementation of the NIST Elliptic Curves over Prime Fields. */ mp_err ec_GFp_pts_mul_jac(const mp_int *k1, const mp_int *k2, 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[4][4][2]; mp_int rz; const mp_int *a, *b; unsigned int i, j; int ai, bi, d; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { MP_DIGITS(&precomp[i][j][0]) = 0; MP_DIGITS(&precomp[i][j][1]) = 0; } } MP_DIGITS(&rz) = 0; ARGCHK(group != NULL, MP_BADARG); ARGCHK(!((k1 == NULL) && ((k2 == NULL) || (px == NULL) || (py == NULL))), MP_BADARG); /* if some arguments are not defined used ECPoint_mul */ if (k1 == NULL) { return ECPoint_mul(group, k2, px, py, rx, ry); } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { return ECPoint_mul(group, k1, NULL, NULL, rx, ry); } /* initialize precomputation table */ for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { MP_CHECKOK(mp_init(&precomp[i][j][0])); MP_CHECKOK(mp_init(&precomp[i][j][1])); } } /* fill precomputation table */ /* assign {k1, k2} = {a, b} such that len(a) >= len(b) */ if (mpl_significant_bits(k1) < mpl_significant_bits(k2)) { a = k2; b = k1; if (group->meth->field_enc) { MP_CHECKOK(group->meth-> field_enc(px, &precomp[1][0][0], group->meth)); MP_CHECKOK(group->meth-> field_enc(py, &precomp[1][0][1], group->meth)); } else { MP_CHECKOK(mp_copy(px, &precomp[1][0][0])); MP_CHECKOK(mp_copy(py, &precomp[1][0][1])); } MP_CHECKOK(mp_copy(&group->genx, &precomp[0][1][0])); MP_CHECKOK(mp_copy(&group->geny, &precomp[0][1][1])); } else { a = k1; b = k2; MP_CHECKOK(mp_copy(&group->genx, &precomp[1][0][0])); MP_CHECKOK(mp_copy(&group->geny, &precomp[1][0][1])); if (group->meth->field_enc) { MP_CHECKOK(group->meth-> field_enc(px, &precomp[0][1][0], group->meth)); MP_CHECKOK(group->meth-> field_enc(py, &precomp[0][1][1], group->meth)); } else { MP_CHECKOK(mp_copy(px, &precomp[0][1][0])); MP_CHECKOK(mp_copy(py, &precomp[0][1][1])); } } /* precompute [*][0][*] */ mp_zero(&precomp[0][0][0]); mp_zero(&precomp[0][0][1]); MP_CHECKOK(group-> point_dbl(&precomp[1][0][0], &precomp[1][0][1], &precomp[2][0][0], &precomp[2][0][1], group)); MP_CHECKOK(group-> point_add(&precomp[1][0][0], &precomp[1][0][1], &precomp[2][0][0], &precomp[2][0][1], &precomp[3][0][0], &precomp[3][0][1], group)); /* precompute [*][1][*] */ for (i = 1; i < 4; i++) { MP_CHECKOK(group-> point_add(&precomp[0][1][0], &precomp[0][1][1], &precomp[i][0][0], &precomp[i][0][1], &precomp[i][1][0], &precomp[i][1][1], group)); } /* precompute [*][2][*] */ MP_CHECKOK(group-> point_dbl(&precomp[0][1][0], &precomp[0][1][1], &precomp[0][2][0], &precomp[0][2][1], group)); for (i = 1; i < 4; i++) { MP_CHECKOK(group-> point_add(&precomp[0][2][0], &precomp[0][2][1], &precomp[i][0][0], &precomp[i][0][1], &precomp[i][2][0], &precomp[i][2][1], group)); } /* precompute [*][3][*] */ MP_CHECKOK(group-> point_add(&precomp[0][1][0], &precomp[0][1][1], &precomp[0][2][0], &precomp[0][2][1], &precomp[0][3][0], &precomp[0][3][1], group)); for (i = 1; i < 4; i++) { MP_CHECKOK(group-> point_add(&precomp[0][3][0], &precomp[0][3][1], &precomp[i][0][0], &precomp[i][0][1], &precomp[i][3][0], &precomp[i][3][1], group)); } d = (mpl_significant_bits(a) + 1) / 2; /* R = inf */ MP_CHECKOK(mp_init(&rz)); MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); for (i = d; i-- > 0;) { ai = MP_GET_BIT(a, 2 * i + 1); ai <<= 1; ai |= MP_GET_BIT(a, 2 * i); bi = MP_GET_BIT(b, 2 * i + 1); bi <<= 1; bi |= MP_GET_BIT(b, 2 * i); /* R = 2^2 * R */ MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); /* R = R + (ai * A + bi * B) */ MP_CHECKOK(ec_GFp_pt_add_jac_aff (rx, ry, &rz, &precomp[ai][bi][0], &precomp[ai][bi][1], rx, ry, &rz, group)); } MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, 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: mp_clear(&rz); for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { mp_clear(&precomp[i][j][0]); mp_clear(&precomp[i][j][1]); } } return res; }
/* Performs basic tests of elliptic curve cryptography over prime fields. * If tests fail, then it prints an error message, aborts, and returns an * error code. Otherwise, returns 0. */ int ectest_curve_GFp(ECGroup *group, int ectestPrint, int ectestTime, int generic) { mp_int one, order_1, gx, gy, rx, ry, n; int size; mp_err res; char s[1000]; /* initialize values */ MP_CHECKOK(mp_init(&one)); MP_CHECKOK(mp_init(&order_1)); MP_CHECKOK(mp_init(&gx)); MP_CHECKOK(mp_init(&gy)); MP_CHECKOK(mp_init(&rx)); MP_CHECKOK(mp_init(&ry)); MP_CHECKOK(mp_init(&n)); MP_CHECKOK(mp_set_int(&one, 1)); MP_CHECKOK(mp_sub(&group->order, &one, &order_1)); /* encode base point */ if (group->meth->field_dec) { MP_CHECKOK(group->meth->field_dec(&group->genx, &gx, group->meth)); MP_CHECKOK(group->meth->field_dec(&group->geny, &gy, group->meth)); } else { MP_CHECKOK(mp_copy(&group->genx, &gx)); MP_CHECKOK(mp_copy(&group->geny, &gy)); } if (ectestPrint) { /* output base point */ printf(" base point P:\n"); MP_CHECKOK(mp_toradix(&gx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&gy, s, 16)); printf(" %s\n", s); if (group->meth->field_enc) { printf(" base point P (encoded):\n"); MP_CHECKOK(mp_toradix(&group->genx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&group->geny, s, 16)); printf(" %s\n", s); } } #ifdef ECL_ENABLE_GFP_PT_MUL_AFF /* multiply base point by order - 1 and check for negative of base * point */ MP_CHECKOK(ec_GFp_pt_mul_aff(&order_1, &group->genx, &group->geny, &rx, &ry, group)); if (ectestPrint) { printf(" (order-1)*P (affine):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } MP_CHECKOK(group->meth->field_neg(&ry, &ry, group->meth)); if ((mp_cmp(&rx, &group->genx) != 0) || (mp_cmp(&ry, &group->geny) != 0)) { printf(" Error: invalid result (expected (- base point)).\n"); res = MP_NO; goto CLEANUP; } #endif #ifdef ECL_ENABLE_GFP_PT_MUL_AFF /* multiply base point by order - 1 and check for negative of base * point */ MP_CHECKOK(ec_GFp_pt_mul_jac(&order_1, &group->genx, &group->geny, &rx, &ry, group)); if (ectestPrint) { printf(" (order-1)*P (jacobian):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } MP_CHECKOK(group->meth->field_neg(&ry, &ry, group->meth)); if ((mp_cmp(&rx, &group->genx) != 0) || (mp_cmp(&ry, &group->geny) != 0)) { printf(" Error: invalid result (expected (- base point)).\n"); res = MP_NO; goto CLEANUP; } #endif /* multiply base point by order - 1 and check for negative of base * point */ MP_CHECKOK(ECPoint_mul(group, &order_1, NULL, NULL, &rx, &ry)); if (ectestPrint) { printf(" (order-1)*P (ECPoint_mul):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { printf(" Error: invalid result (expected (- base point)).\n"); res = MP_NO; goto CLEANUP; } /* multiply base point by order - 1 and check for negative of base * point */ MP_CHECKOK(ECPoint_mul(group, &order_1, &gx, &gy, &rx, &ry)); if (ectestPrint) { printf(" (order-1)*P (ECPoint_mul):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { printf(" Error: invalid result (expected (- base point)).\n"); res = MP_NO; goto CLEANUP; } #ifdef ECL_ENABLE_GFP_PT_MUL_AFF /* multiply base point by order and check for point at infinity */ MP_CHECKOK(ec_GFp_pt_mul_aff(&group->order, &group->genx, &group->geny, &rx, &ry, group)); if (ectestPrint) { printf(" (order)*P (affine):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { printf(" Error: invalid result (expected point at infinity).\n"); res = MP_NO; goto CLEANUP; } #endif #ifdef ECL_ENABLE_GFP_PT_MUL_JAC /* multiply base point by order and check for point at infinity */ MP_CHECKOK(ec_GFp_pt_mul_jac(&group->order, &group->genx, &group->geny, &rx, &ry, group)); if (ectestPrint) { printf(" (order)*P (jacobian):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { printf(" Error: invalid result (expected point at infinity).\n"); res = MP_NO; goto CLEANUP; } #endif /* multiply base point by order and check for point at infinity */ MP_CHECKOK(ECPoint_mul(group, &group->order, NULL, NULL, &rx, &ry)); if (ectestPrint) { printf(" (order)*P (ECPoint_mul):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { printf(" Error: invalid result (expected point at infinity).\n"); res = MP_NO; goto CLEANUP; } /* multiply base point by order and check for point at infinity */ MP_CHECKOK(ECPoint_mul(group, &group->order, &gx, &gy, &rx, &ry)); if (ectestPrint) { printf(" (order)*P (ECPoint_mul):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { printf(" Error: invalid result (expected point at infinity).\n"); res = MP_NO; goto CLEANUP; } /* check that (order-1)P + (order-1)P + P == (order-1)P */ MP_CHECKOK(ECPoints_mul(group, &order_1, &order_1, &gx, &gy, &rx, &ry)); MP_CHECKOK(ECPoints_mul(group, &one, &one, &rx, &ry, &rx, &ry)); if (ectestPrint) { printf(" (order-1)*P + (order-1)*P + P == (order-1)*P (ECPoints_mul):\n"); MP_CHECKOK(mp_toradix(&rx, s, 16)); printf(" %s\n", s); MP_CHECKOK(mp_toradix(&ry, s, 16)); printf(" %s\n", s); } MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { printf(" Error: invalid result (expected (- base point)).\n"); res = MP_NO; goto CLEANUP; } /* test validate_point function */ if (ECPoint_validate(group, &gx, &gy) != MP_YES) { printf(" Error: validate point on base point failed.\n"); res = MP_NO; goto CLEANUP; } MP_CHECKOK(mp_add_d(&gy, 1, &ry)); if (ECPoint_validate(group, &gx, &ry) != MP_NO) { printf(" Error: validate point on invalid point passed.\n"); res = MP_NO; goto CLEANUP; } if (ectestTime) { /* compute random scalar */ size = mpl_significant_bits(&group->meth->irr); if (size < MP_OKAY) { goto CLEANUP; } MP_CHECKOK(mpp_random_size(&n, (size + ECL_BITS - 1) / ECL_BITS)); MP_CHECKOK(group->meth->field_mod(&n, &n, group->meth)); /* timed test */ if (generic) { #ifdef ECL_ENABLE_GFP_PT_MUL_AFF M_TimeOperation(MP_CHECKOK(ec_GFp_pt_mul_aff(&n, &group->genx, &group->geny, &rx, &ry, group)), 100); #endif M_TimeOperation(MP_CHECKOK(ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), 100); M_TimeOperation(MP_CHECKOK(ECPoints_mul(group, &n, &n, &gx, &gy, &rx, &ry)), 100); } else { M_TimeOperation(MP_CHECKOK(ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), 100); M_TimeOperation(MP_CHECKOK(ECPoint_mul(group, &n, &gx, &gy, &rx, &ry)), 100); M_TimeOperation(MP_CHECKOK(ECPoints_mul(group, &n, &n, &gx, &gy, &rx, &ry)), 100); } } CLEANUP: mp_clear(&one); mp_clear(&order_1); mp_clear(&gx); mp_clear(&gy); mp_clear(&rx); mp_clear(&ry); mp_clear(&n); if (res != MP_OKAY) { printf(" Error: exiting with error value %i\n", res); } return res; }
/* Validates a point on a GFp curve. */ mp_err ec_GFp_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group) { mp_err res = MP_NO; mp_int accl, accr, tmp, pxt, pyt; MP_DIGITS(&accl) = 0; MP_DIGITS(&accr) = 0; MP_DIGITS(&tmp) = 0; MP_DIGITS(&pxt) = 0; MP_DIGITS(&pyt) = 0; MP_CHECKOK(mp_init(&accl)); MP_CHECKOK(mp_init(&accr)); MP_CHECKOK(mp_init(&tmp)); MP_CHECKOK(mp_init(&pxt)); MP_CHECKOK(mp_init(&pyt)); /* 1: Verify that publicValue is not the point at infinity */ if (ec_GFp_pt_is_inf_aff(px, py) == MP_YES) { res = MP_NO; goto CLEANUP; } /* 2: Verify that the coordinates of publicValue are elements * of the field. */ if ((MP_SIGN(px) == MP_NEG) || (mp_cmp(px, &group->meth->irr) >= 0) || (MP_SIGN(py) == MP_NEG) || (mp_cmp(py, &group->meth->irr) >= 0)) { res = MP_NO; goto CLEANUP; } /* 3: Verify that publicValue is on the curve. */ if (group->meth->field_enc) { group->meth->field_enc(px, &pxt, group->meth); group->meth->field_enc(py, &pyt, group->meth); } else { MP_CHECKOK(mp_copy(px, &pxt)); MP_CHECKOK(mp_copy(py, &pyt)); } /* left-hand side: y^2 */ MP_CHECKOK(group->meth->field_sqr(&pyt, &accl, group->meth)); /* right-hand side: x^3 + a*x + b = (x^2 + a)*x + b by Horner's rule */ MP_CHECKOK(group->meth->field_sqr(&pxt, &tmp, group->meth)); MP_CHECKOK(group->meth->field_add(&tmp, &group->curvea, &tmp, group->meth)); MP_CHECKOK(group->meth->field_mul(&tmp, &pxt, &accr, group->meth)); MP_CHECKOK(group->meth->field_add(&accr, &group->curveb, &accr, group->meth)); /* check LHS - RHS == 0 */ MP_CHECKOK(group->meth->field_sub(&accl, &accr, &accr, group->meth)); if (mp_cmp_z(&accr) != 0) { res = MP_NO; goto CLEANUP; } /* 4: Verify that the order of the curve times the publicValue * is the point at infinity. */ MP_CHECKOK(ECPoint_mul(group, &group->order, px, py, &pxt, &pyt)); if (ec_GFp_pt_is_inf_aff(&pxt, &pyt) != MP_YES) { res = MP_NO; goto CLEANUP; } res = MP_YES; CLEANUP: mp_clear(&accl); mp_clear(&accr); mp_clear(&tmp); mp_clear(&pxt); mp_clear(&pyt); return res; }