static void ec_GFp_simple_mul_single(const EC_GROUP *group, EC_RAW_POINT *r, const EC_RAW_POINT *p, const EC_SCALAR *scalar) { // This is a generic implementation for uncommon curves that not do not // warrant a tuned one. It uses unsigned digits so that the doubling case in // |ec_GFp_simple_add| is always unreachable, erring on safety and simplicity. // Compute a table of the first 32 multiples of |p| (including infinity). EC_RAW_POINT precomp[32]; ec_GFp_simple_point_set_to_infinity(group, &precomp[0]); ec_GFp_simple_point_copy(&precomp[1], p); for (size_t j = 2; j < OPENSSL_ARRAY_SIZE(precomp); j++) { if (j & 1) { ec_GFp_simple_add(group, &precomp[j], &precomp[1], &precomp[j - 1]); } else { ec_GFp_simple_dbl(group, &precomp[j], &precomp[j / 2]); } } // Divide bits in |scalar| into windows. unsigned bits = BN_num_bits(&group->order); int r_is_at_infinity = 1; for (unsigned i = bits - 1; i < bits; i--) { if (!r_is_at_infinity) { ec_GFp_simple_dbl(group, r, r); } if (i % 5 == 0) { // Compute the next window value. const size_t width = group->order.width; uint8_t window = bn_is_bit_set_words(scalar->words, width, i + 4) << 4; window |= bn_is_bit_set_words(scalar->words, width, i + 3) << 3; window |= bn_is_bit_set_words(scalar->words, width, i + 2) << 2; window |= bn_is_bit_set_words(scalar->words, width, i + 1) << 1; window |= bn_is_bit_set_words(scalar->words, width, i); // Select the entry in constant-time. EC_RAW_POINT tmp; OPENSSL_memset(&tmp, 0, sizeof(EC_RAW_POINT)); for (size_t j = 0; j < OPENSSL_ARRAY_SIZE(precomp); j++) { BN_ULONG mask = constant_time_eq_w(j, window); ec_felem_select(group, &tmp.X, mask, &precomp[j].X, &tmp.X); ec_felem_select(group, &tmp.Y, mask, &precomp[j].Y, &tmp.Y); ec_felem_select(group, &tmp.Z, mask, &precomp[j].Z, &tmp.Z); } if (r_is_at_infinity) { ec_GFp_simple_point_copy(r, &tmp); r_is_at_infinity = 0; } else { ec_GFp_simple_add(group, r, r, &tmp); } } } if (r_is_at_infinity) { ec_GFp_simple_point_set_to_infinity(group, r); } }
int EC_POINT_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a, BN_CTX *ctx) { if ((group->meth != r->meth) || (r->meth != a->meth)) { OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS); return 0; } return ec_GFp_simple_dbl(group, r, a, ctx); }
int ec_GFp_simple_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a, const EC_POINT *b, BN_CTX *ctx) { int (*field_mul)(const EC_GROUP *, BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); int (*field_sqr)(const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *); const BIGNUM *p; BN_CTX *new_ctx = NULL; BIGNUM *n0, *n1, *n2, *n3, *n4, *n5, *n6; int ret = 0; if (a == b) { return ec_GFp_simple_dbl(group, r, a, ctx); } if (EC_POINT_is_at_infinity(group, a)) { return ec_GFp_simple_point_copy(r, b); } if (EC_POINT_is_at_infinity(group, b)) { return ec_GFp_simple_point_copy(r, a); } field_mul = group->meth->field_mul; field_sqr = group->meth->field_sqr; p = &group->field; if (ctx == NULL) { ctx = new_ctx = BN_CTX_new(); if (ctx == NULL) { return 0; } } BN_CTX_start(ctx); n0 = BN_CTX_get(ctx); n1 = BN_CTX_get(ctx); n2 = BN_CTX_get(ctx); n3 = BN_CTX_get(ctx); n4 = BN_CTX_get(ctx); n5 = BN_CTX_get(ctx); n6 = BN_CTX_get(ctx); if (n6 == NULL) { goto end; } /* Note that in this function we must not read components of 'a' or 'b' * once we have written the corresponding components of 'r'. * ('r' might be one of 'a' or 'b'.) */ /* n1, n2 */ int b_Z_is_one = BN_cmp(&b->Z, &group->one) == 0; if (b_Z_is_one) { if (!BN_copy(n1, &a->X) || !BN_copy(n2, &a->Y)) { goto end; } /* n1 = X_a */ /* n2 = Y_a */ } else { if (!field_sqr(group, n0, &b->Z, ctx) || !field_mul(group, n1, &a->X, n0, ctx)) { goto end; } /* n1 = X_a * Z_b^2 */ if (!field_mul(group, n0, n0, &b->Z, ctx) || !field_mul(group, n2, &a->Y, n0, ctx)) { goto end; } /* n2 = Y_a * Z_b^3 */ } /* n3, n4 */ int a_Z_is_one = BN_cmp(&a->Z, &group->one) == 0; if (a_Z_is_one) { if (!BN_copy(n3, &b->X) || !BN_copy(n4, &b->Y)) { goto end; } /* n3 = X_b */ /* n4 = Y_b */ } else { if (!field_sqr(group, n0, &a->Z, ctx) || !field_mul(group, n3, &b->X, n0, ctx)) { goto end; } /* n3 = X_b * Z_a^2 */ if (!field_mul(group, n0, n0, &a->Z, ctx) || !field_mul(group, n4, &b->Y, n0, ctx)) { goto end; } /* n4 = Y_b * Z_a^3 */ } /* n5, n6 */ if (!BN_mod_sub_quick(n5, n1, n3, p) || !BN_mod_sub_quick(n6, n2, n4, p)) { goto end; } /* n5 = n1 - n3 */ /* n6 = n2 - n4 */ if (BN_is_zero(n5)) { if (BN_is_zero(n6)) { /* a is the same point as b */ BN_CTX_end(ctx); ret = ec_GFp_simple_dbl(group, r, a, ctx); ctx = NULL; goto end; } else { /* a is the inverse of b */ BN_zero(&r->Z); ret = 1; goto end; } } /* 'n7', 'n8' */ if (!BN_mod_add_quick(n1, n1, n3, p) || !BN_mod_add_quick(n2, n2, n4, p)) { goto end; } /* 'n7' = n1 + n3 */ /* 'n8' = n2 + n4 */ /* Z_r */ if (a_Z_is_one && b_Z_is_one) { if (!BN_copy(&r->Z, n5)) { goto end; } } else { if (a_Z_is_one) { if (!BN_copy(n0, &b->Z)) { goto end; } } else if (b_Z_is_one) { if (!BN_copy(n0, &a->Z)) { goto end; } } else if (!field_mul(group, n0, &a->Z, &b->Z, ctx)) { goto end; } if (!field_mul(group, &r->Z, n0, n5, ctx)) { goto end; } } /* Z_r = Z_a * Z_b * n5 */ /* X_r */ if (!field_sqr(group, n0, n6, ctx) || !field_sqr(group, n4, n5, ctx) || !field_mul(group, n3, n1, n4, ctx) || !BN_mod_sub_quick(&r->X, n0, n3, p)) { goto end; } /* X_r = n6^2 - n5^2 * 'n7' */ /* 'n9' */ if (!BN_mod_lshift1_quick(n0, &r->X, p) || !BN_mod_sub_quick(n0, n3, n0, p)) { goto end; } /* n9 = n5^2 * 'n7' - 2 * X_r */ /* Y_r */ if (!field_mul(group, n0, n0, n6, ctx) || !field_mul(group, n5, n4, n5, ctx)) { goto end; /* now n5 is n5^3 */ } if (!field_mul(group, n1, n2, n5, ctx) || !BN_mod_sub_quick(n0, n0, n1, p)) { goto end; } if (BN_is_odd(n0) && !BN_add(n0, n0, p)) { goto end; } /* now 0 <= n0 < 2*p, and n0 is even */ if (!BN_rshift1(&r->Y, n0)) { goto end; } /* Y_r = (n6 * 'n9' - 'n8' * 'n5^3') / 2 */ ret = 1; end: if (ctx) { /* otherwise we already called BN_CTX_end */ BN_CTX_end(ctx); } BN_CTX_free(new_ctx); return ret; }