void fp_invn_low(dig_t *c, const dig_t *a) { mp_size_t cn; align dig_t s[FP_DIGS], t[2 * FP_DIGS], u[FP_DIGS + 1]; #if FP_RDC == MONTY dv_zero(t + FP_DIGS, FP_DIGS); dv_copy(t, a, FP_DIGS); fp_rdcn_low(u, t); #else fp_copy(u, a); #endif dv_copy(s, fp_prime_get(), FP_DIGS); mpn_gcdext(t, c, &cn, u, FP_DIGS, s, FP_DIGS); if (cn < 0) { dv_zero(c - cn, FP_DIGS + cn); mpn_sub_n(c, fp_prime_get(), c, FP_DIGS); } else { dv_zero(c + cn, FP_DIGS - cn); } #if FP_RDC == MONTY dv_zero(t, FP_DIGS); dv_copy(t + FP_DIGS, c, FP_DIGS); mpn_tdiv_qr(u, c, 0, t, 2 * FP_DIGS, fp_prime_get(), FP_DIGS); #endif }
/** * Multiplies two binary field elements using shift-and-add multiplication. * * @param c - the result. * @param a - the first binary field element. * @param b - the second binary field element. * @param size - the number of digits to multiply. */ static void fb_mul_basic_imp(dig_t *c, const dig_t *a, const dig_t *b, int size) { int i; dv_t s; dv_null(s); TRY { /* We need a temporary variable so that c can be a or b. */ dv_new(s); dv_zero(s, 2 * FB_DIGS); dv_copy(s, b, size); dv_zero(c, 2 * size); if (a[0] & 1) { dv_copy(c, b, size); } for (i = 1; i <= (FB_DIGIT * size) - 1; i++) { fb_lsh1_low(s, s); fb_rdc(s, s); if (fb_get_bit(a, i)) { fb_add(c, c, s); } } } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(s); } }
void fp_prime_back(bn_t c, const fp_t a) { dv_t t; int i; dv_null(t); TRY { dv_new(t); bn_grow(c, FP_DIGS); for (i = 0; i < FP_DIGS; i++) { c->dp[i] = a[i]; } #if FP_RDC == MONTY dv_zero(t, 2 * FP_DIGS + 1); dv_copy(t, a, FP_DIGS); fp_rdc(c->dp, t); #endif c->used = FP_DIGS; c->sign = BN_POS; bn_trim(c); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
/** * Multiplies two binary field elements using right-to-left comb multiplication. * * @param c - the result. * @param a - the first binary field element. * @param b - the second binary field element. * @param size - the number of digits to multiply. */ static void fb_mul_rcomb_imp(dig_t *c, const dig_t *a, const dig_t *b, int size) { dv_t _b; dv_null(_b); TRY { dv_new(_b); dv_zero(c, 2 * size); for (int i = 0; i < size; i++) _b[i] = b[i]; _b[size] = 0; for (int i = 0; i < FB_DIGIT; i++) { for (int j = 0; j < size; j++) { if (a[j] & ((dig_t)1 << i)) { fb_addd_low(c + j, c + j, _b, size + 1); } } if (i != FB_DIGIT - 1) { bn_lsh1_low(_b, _b, size + 1); } } } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(_b); } }
void fb_mul_lcomb(fb_t c, const fb_t a, const fb_t b) { dv_t t; dig_t carry; dv_null(t); TRY { dv_new(t); dv_zero(t, 2 * FB_DIGS); for (int i = FB_DIGIT - 1; i >= 0; i--) { for (int j = 0; j < FB_DIGS; j++) { if (a[j] & ((dig_t)1 << i)) { /* This cannot use fb_addn_low() because there is no * guarantee that operands will be aligned. */ fb_addd_low(t + j, t + j, b, FB_DIGS); } } if (i != 0) { carry = fb_lsh1_low(t, t); fb_lsh1_low(t + FB_DIGS, t + FB_DIGS); t[FB_DIGS] |= carry; } } fb_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
void fp_mul_karat(fp_t c, const fp_t a, const fp_t b) { dv_t t; dv_null(t); TRY { /* We need a temporary variable so that c can be a or b. */ dv_new(t); dv_zero(t, 2 * FP_DIGS); if (FP_DIGS > 1) { fp_mul_karat_imp(t, a, b, FP_DIGS, FP_KARAT); } else { fp_muln_low(t, a, b); } fp_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
void fp_prime_conv_dig(fp_t c, dig_t a) { dv_t t; ctx_t *ctx = core_get(); bn_null(t); TRY { dv_new(t); #if FP_RDC == MONTY if (a != 1) { dv_zero(t, 2 * FP_DIGS + 1); t[FP_DIGS] = fp_mul1_low(t, ctx->conv.dp, a); fp_rdc(c, t); } else { dv_copy(c, ctx->one.dp, FP_DIGS); } #else (void)ctx; fp_zero(c); c[0] = a; #endif } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
void fp_mul_basic(fp_t c, const fp_t a, const fp_t b) { int i; dv_t t; dig_t carry; dv_null(t); TRY { /* We need a temporary variable so that c can be a or b. */ dv_new(t); dv_zero(t, 2 * FP_DIGS); for (i = 0; i < FP_DIGS; i++) { carry = fp_mula_low(t + i, b, *(a + i)); *(t + i + FP_DIGS) = carry; } fp_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
void fp2_nord_low(dv2_t c, dv2_t a) { dv2_t t; bn_t b; dv2_null(t); bn_null(b); TRY { dv2_new(t); bn_new(b); #ifdef FP_QNRES /* If p = 3 mod 8, (1 + i) is a QNR/CNR. */ /* (a_0 + a_1 * i) * (1 + i) = (a_0 - a_1) + (a_0 + a_1) * u. */ dv_copy(t[0], a[1], 2 * FP_DIGS); fp_addc_low(c[1], a[0], a[1]); fp_subc_low(c[0], a[0], t[0]); #else switch (fp_prime_get_mod8()) { case 3: /* If p = 3 mod 8, (1 + u) is a QNR, u^2 = -1. */ /* (a_0 + a_1 * u) * (1 + u) = (a_0 - a_1) + (a_0 + a_1) * u. */ dv_copy(t[0], a[1], 2 * FP_DIGS); fp_addc_low(c[1], a[0], a[1]); fp_subc_low(c[0], a[0], t[0]); break; case 1: case 5: /* If p = 1,5 mod 8, (u) is a QNR. */ dv_copy(t[0], a[0], 2 * FP_DIGS); dv_zero(t[1], FP_DIGS); dv_copy(t[1] + FP_DIGS, fp_prime_get(), FP_DIGS); fp_subc_low(c[0], t[1], a[1]); for (int i = -1; i > fp_prime_get_qnr(); i--) { fp_subc_low(c[0], c[0], a[1]); } dv_copy(c[1], t[0], 2 * FP_DIGS); break; case 7: /* If p = 7 mod 8, (2 + u) is a QNR/CNR. */ fp2_addc_low(t, a, a); fp_subc_low(c[0], t[0], a[1]); fp_addc_low(c[1], t[1], a[0]); break; default: THROW(ERR_NO_VALID); break; } #endif } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv2_free(t); bn_free(b); } }
/** * Multiplies two binary field elements using left-to-right comb multiplication. * * @param c - the result. * @param a - the first binary field element. * @param b - the second binary field element. * @param size - the number of digits to multiply. */ static void fb_mul_lcomb_imp(dig_t *c, const dig_t *a, const dig_t *b, int size) { dv_zero(c, 2 * size); for (int i = FB_DIGIT - 1; i >= 0; i--) { for (int j = 0; j < size; j++) { if (a[j] & ((dig_t)1 << i)) { fb_addd_low(c + j, c + j, b, size); } } if (i != 0) { bn_lsh1_low(c, c, 2 * size); } } }
void fb_mul_rcomb(fb_t c, const fb_t a, const fb_t b) { dv_t t, _b; dig_t carry; dv_null(t); dv_null(_b); TRY { dv_new(t); dv_new(_b); dv_zero(t, 2 * FB_DIGS); dv_zero(_b, FB_DIGS + 1); fb_copy(_b, b); for (int i = 0; i < FB_DIGIT; i++) { for (int j = 0; j < FB_DIGS; j++) { if (a[j] & ((dig_t)1 << i)) { fb_addd_low(t + j, t + j, _b, FB_DIGS + 1); } } if (i != FB_DIGIT - 1) { carry = fb_lsh1_low(_b, _b); _b[FB_DIGS] = (_b[FB_DIGS] << 1) | carry; } } fb_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); dv_free(_b); } }
void fb_mul_basic(fb_t c, const fb_t a, const fb_t b) { int i; dv_t s; fb_t t; dv_null(s); fb_null(t); TRY { /* We need a temporary variable so that c can be a or b. */ fb_new(t); dv_new(s); fb_zero(t); dv_zero(s + FB_DIGS, FB_DIGS); fb_copy(s, b); if (a[0] & 1) { fb_copy(t, b); } for (i = 1; i < FB_BITS; i++) { /* We are already shifting a temporary value, so this is more efficient * than calling fb_lsh(). */ s[FB_DIGS] = fb_lsh1_low(s, s); fb_rdc(s, s); if (fb_get_bit(a, i)) { fb_add(t, t, s); } } if (fb_bits(t) > FB_BITS) { fb_poly_add(c, t); } else { fb_copy(c, t); } } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { fb_free(t); fb_free(s); } }
void fb_mul_karat(fb_t c, const fb_t a, const fb_t b) { dv_t t; dv_null(t); TRY { /* We need a temporary variable so that c can be a or b. */ dv_new(t); dv_zero(t, 2 * FB_DIGS); fb_mul_karat_imp(t, a, b, FB_DIGS, FB_KARAT); fb_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
void fp_sqr_basic(fp_t c, const fp_t a) { int i; dv_t t; dv_null(t); TRY { dv_new(t); dv_zero(t, 2 * RLC_FP_DIGS); for (i = 0; i < RLC_FP_DIGS; i++) { bn_sqra_low(t + (2 * i), a + i, RLC_FP_DIGS - i); } fp_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { fp_free(t); } }
void fp_sqr_karat(fp_t c, const fp_t a) { dv_t t; dv_null(t); TRY { dv_new(t); dv_zero(t, 2 * RLC_FP_DIGS); if (RLC_FP_DIGS > 1) { fp_sqr_karat_imp(t, a, RLC_FP_DIGS, FP_KARAT); } else { fp_sqrn_low(t, a); } fp_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
/** * Assigns the prime field modulus. * * @param[in] p - the new prime field modulus. */ static void fp_prime_set(const bn_t p) { dv_t s, q; bn_t t; ctx_t *ctx = core_get(); if (p->used != FP_DIGS) { THROW(ERR_NO_VALID); } dv_null(s); bn_null(t); dv_null(q); TRY { dv_new(s); bn_new(t); dv_new(q); bn_copy(&(ctx->prime), p); bn_mod_dig(&(ctx->mod8), &(ctx->prime), 8); switch (ctx->mod8) { case 3: case 7: ctx->qnr = -1; /* The current code for extensions of Fp^3 relies on qnr being * also a cubic non-residue. */ ctx->cnr = 0; break; case 1: case 5: ctx->qnr = ctx->cnr = -2; break; default: ctx->qnr = ctx->cnr = 0; THROW(ERR_NO_VALID); break; } #ifdef FP_QNRES if (ctx->mod8 != 3) { THROW(ERR_NO_VALID); } #endif #if FP_RDC == MONTY || !defined(STRIP) bn_mod_pre_monty(t, &(ctx->prime)); ctx->u = t->dp[0]; dv_zero(s, 2 * FP_DIGS); s[2 * FP_DIGS] = 1; dv_zero(q, 2 * FP_DIGS + 1); dv_copy(q, ctx->prime.dp, FP_DIGS); bn_divn_low(t->dp, ctx->conv.dp, s, 2 * FP_DIGS + 1, q, FP_DIGS); ctx->conv.used = FP_DIGS; bn_trim(&(ctx->conv)); bn_set_dig(&(ctx->one), 1); bn_lsh(&(ctx->one), &(ctx->one), ctx->prime.used * BN_DIGIT); bn_mod(&(ctx->one), &(ctx->one), &(ctx->prime)); #endif fp_prime_calc(); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { bn_free(t); dv_free(s); dv_free(q); } }
void fb_invn_low(dig_t *c, const dig_t *a) { int j, d, lu, lv, lt, l1, l2, bu, bv; align dig_t _u[2 * FB_DIGS], _v[2 * FB_DIGS]; align dig_t _g1[2 * FB_DIGS], _g2[2 * FB_DIGS]; dig_t *t = NULL, *u = NULL, *v = NULL, *g1 = NULL, *g2 = NULL, carry; dv_zero(_g1, FB_DIGS + 1); dv_zero(_g2, FB_DIGS + 1); u = _u; v = _v; g1 = _g1; g2 = _g2; /* u = a, v = f, g1 = 1, g2 = 0. */ dv_copy(u, a, FB_DIGS); dv_copy(v, fb_poly_get(), FB_DIGS); g1[0] = 1; lu = lv = FB_DIGS; l1 = l2 = 1; bu = fb_bits(u); bv = FB_BITS + 1; j = bu - bv; /* While (u != 1). */ while (1) { /* If j < 0 then swap(u, v), swap(g1, g2), j = -j. */ if (j < 0) { t = u; u = v; v = t; lt = lu; lu = lv; lv = lt; t = g1; g1 = g2; g2 = t; lt = l1; l1 = l2; l2 = lt; j = -j; } SPLIT(j, d, j, FB_DIG_LOG); /* u = u + v * z^j. */ if (j > 0) { carry = fb_lsha_low(u + d, v, j, lv); u[d + lv] ^= carry; } else { fb_addd_low(u + d, u + d, v, lv); } /* g1 = g1 + g2 * z^j. */ if (j > 0) { carry = fb_lsha_low(g1 + d, g2, j, l2); l1 = (l2 + d >= l1 ? l2 + d : l1); if (carry) { g1[d + l2] ^= carry; l1 = (l2 + d >= l1 ? l1 + 1 : l1); } } else { fb_addd_low(g1 + d, g1 + d, g2, l2); l1 = (l2 + d > l1 ? l2 + d : l1); } while (u[lu - 1] == 0) lu--; while (v[lv - 1] == 0) lv--; if (lu == 1 && u[0] == 1) break; /* j = deg(u) - deg(v). */ lt = util_bits_dig(u[lu - 1]) - util_bits_dig(v[lv - 1]); j = ((lu - lv) << FB_DIG_LOG) + lt; } /* Return g1. */ fb_copy(c, g1); }
void bn_zero(bn_t a) { a->sign = BN_POS; a->used = 1; dv_zero(a->dp, a->alloc); }
void fp2_nord_low(dv2_t c, dv2_t a) { dv2_t t; bn_t b; dv2_null(t); bn_null(b); TRY { dv2_new(t); bn_new(b); #if FP_PRIME == 158 fp_addc_low(t[0], a[0], a[0]); fp_addc_low(t[0], t[0], t[0]); fp_subc_low(t[0], t[0], a[1]); fp_addc_low(t[1], a[1], a[1]); fp_addc_low(t[1], t[1], t[1]); fp_addc_low(c[1], a[0], t[1]); dv_copy(c[0], t[0], 2 * FP_DIGS); #elif defined(FP_QNRES) /* If p = 3 mod 8, (1 + i) is a QNR/CNR. */ /* (a_0 + a_1 * i) * (1 + i) = (a_0 - a_1) + (a_0 + a_1) * u. */ dv_copy(t[0], a[1], 2 * FP_DIGS); fp_addc_low(c[1], a[0], a[1]); fp_subc_low(c[0], a[0], t[0]); #else switch (fp_prime_get_mod8()) { case 3: /* If p = 3 mod 8, (1 + u) is a QNR, u^2 = -1. */ /* (a_0 + a_1 * u) * (1 + u) = (a_0 - a_1) + (a_0 + a_1) * u. */ dv_copy(t[0], a[1], 2 * FP_DIGS); fp_addc_low(c[1], a[0], a[1]); fp_subc_low(c[0], a[0], t[0]); break; case 5: /* If p = 5 mod 8, (u) is a QNR. */ dv_copy(t[0], a[0], 2 * FP_DIGS); dv_zero(t[1], FP_DIGS); dv_copy(t[1] + FP_DIGS, fp_prime_get(), FP_DIGS); fp_subc_low(c[0], t[1], a[1]); for (int i = -1; i > fp_prime_get_qnr(); i--) { fp_subc_low(c[0], c[0], a[1]); } dv_copy(c[1], t[0], 2 * FP_DIGS); break; case 7: /* If p = 7 mod 8, (2^lg_4(b-1) + u) is a QNR/CNR. */ /* (a_0 + a_1 * u)(2^lg_4(b-1) + u) = * (2^lg_4(b-1)a_0 - a_1) + (a_0 + 2^lg_4(b-1)a_1 * u. */ fp2_addc_low(t, a, a); fp_prime_back(b, ep_curve_get_b()); for (int i = 1; i < bn_bits(b) / 2; i++) { fp2_addc_low(t, t, t); } fp_subc_low(c[0], t[0], a[1]); fp_addc_low(c[1], t[1], a[0]); break; default: THROW(ERR_NO_VALID); break; } #endif } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv2_free(t); bn_free(b); } }
void fp_rdcs_low(dig_t *c, dig_t *a, dig_t *m) { align dig_t q[2 * FP_DIGS], _q[2 * FP_DIGS]; align dig_t _r[2 * FP_DIGS], r[2 * FP_DIGS], t[2 * FP_DIGS]; int *sform, len; int first, i, j, b0, d0, b1, d1; dig_t carry; sform = fp_prime_get_sps(&len); SPLIT(b0, d0, FP_BITS, FP_DIG_LOG); first = (d0) + (b0 == 0 ? 0 : 1); /* q = floor(a/b^k) */ dv_zero(q, 2 * FP_DIGS); bn_rshd_low(q, a, 2 * FP_DIGS, d0); if (b0 > 0) { bn_rshb_low(q, q, 2 * FP_DIGS, b0); } /* r = a - qb^k. */ dv_copy(r, a, first); if (b0 > 0) { r[first - 1] &= MASK(b0); } carry = 0; while (!fp_is_zero(q)) { dv_zero(_q, 2 * FP_DIGS); for (i = len - 1; i > 0; i--) { j = (sform[i] < 0 ? -sform[i] : sform[i]); SPLIT(b1, d1, j, FP_DIG_LOG); dv_zero(t, 2 * FP_DIGS); bn_lshd_low(t, q, FP_DIGS, d1); if (b1 > 0) { bn_lshb_low(t, t, 2 * FP_DIGS, b1); } if (sform[i] > 0) { bn_subn_low(_q, _q, t, 2 * FP_DIGS); } else { bn_addn_low(_q, _q, t, 2 * FP_DIGS); } } if (sform[0] > 0) { bn_subn_low(_q, _q, q, 2 * FP_DIGS); } else { bn_addn_low(_q, _q, q, 2 * FP_DIGS); } bn_rshd_low(q, _q, 2 * FP_DIGS, d0); if (b0 > 0) { bn_rshb_low(q, q, 2 * FP_DIGS, b0); } dv_copy(_r, _q, first); if (b0 > 0) { _r[first - 1] &= MASK(b0); } fp_add(r, r, _r); } while (fp_cmpn_low(r, m) != CMP_LT) { fp_subn_low(r, r, m); } fp_copy(c, r); }
/** * Computes the square of a multiple precision integer using recursive Karatsuba * squaring. * * @param[out] c - the result. * @param[in] a - the prime field element to square. * @param[in] size - the number of digits to square. * @param[in] level - the number of Karatsuba steps to apply. */ static void fp_sqr_karat_imp(dv_t c, const fp_t a, int size, int level) { int i, h, h1; dv_t t0, t1, a0a0, a1a1; dig_t carry; /* Compute half the digits of a or b. */ h = size >> 1; h1 = size - h; dv_null(t0); dv_null(t1); dv_null(a0a0); dv_null(a1a1); TRY { /* Allocate the temp variables. */ dv_new(t0); dv_new(t1); dv_new(a0a0); dv_new(a1a1); dv_zero(t0, 2 * h1); dv_zero(t1, 2 * (h1 + 1)); dv_zero(a0a0, 2 * h); dv_zero(a1a1, 2 * h1); if (level <= 1) { /* a0a0 = a0 * a0 and a1a1 = a1 * a1 */ #if FP_SQR == BASIC for (i = 0; i < h; i++) { bn_sqra_low(a0a0 + (2 * i), a + i, h - i); } for (i = 0; i < h1; i++) { bn_sqra_low(a1a1 + (2 * i), a + h + i, h1 - i); } #elif FP_SQR == COMBA || FP_SQR == INTEG bn_sqrn_low(a0a0, a, h); bn_sqrn_low(a1a1, a + h, h1); #elif FP_SQR == MULTP bn_muln_low(a0a0, a, a, h); bn_muln_low(a1a1, a + h, a + h, h1); #endif } else { fp_sqr_karat_imp(a0a0, a, h, level - 1); fp_sqr_karat_imp(a1a1, a + h, h1, level - 1); } /* t2 = a1 * a1 << 2*h digits + a0 * a0. */ for (i = 0; i < 2 * h; i++) { c[i] = a0a0[i]; } for (i = 0; i < 2 * h1; i++) { c[2 * h + i] = a1a1[i]; } /* t = (a1 + a0) */ carry = bn_addn_low(t0, a, a + h, h); carry = bn_add1_low(t0 + h, t0 + h, carry, 2); if (h1 > h) { carry = bn_add1_low(t0 + h, t0 + h, *(a + 2 * h), 2); } if (level <= 1) { /* a1a1 = (a1 + a0)*(a1 + a0) */ #if FP_SQR == BASIC for (i = 0; i < h1 + 1; i++) { bn_sqra_low(t1 + (2 * i), t0 + i, h1 + 1 - i); } #elif FP_SQR == COMBA || FP_SQR == INTEG bn_sqrn_low(t1, t0, h1 + 1); #elif FP_SQR == MULTP bn_muln_low(t1, t0, t0, h1 + 1); #endif } else { fp_sqr_karat_imp(t1, t0, h1 + 1, level - 1); } /* t = t - (a0*a0 << h digits) */ carry = bn_subn_low(t1, t1, a0a0, 2 * h); bn_sub1_low(t1 + 2 * h, t1 + 2 * h, carry, 2 * (h1 + 1) - 2 * h); /* t = t - (a1*a1 << h digits) */ carry = bn_subn_low(t1, t1, a1a1, 2 * h1); bn_sub1_low(t1 + 2 * h, t1 + 2 * h, carry, 2 * (h1 + 1) - 2 * h); /* c = c + [(a1 + a0)*(a1 + a0) << digits] */ c += h; carry = bn_addn_low(c, c, t1, 2 * (h1 + 1)); c += 2 * (h1 + 1); carry = bn_add1_low(c, c, carry, 2 * size - h - 2 * (h1 + 1)); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t0); dv_free(t1); dv_free(a0a0); dv_free(a1a1); } }
/** * Multiplies two prime field elements using recursive Karatsuba * multiplication. * * @param[out] c - the result. * @param[in] a - the first prime field element. * @param[in] b - the second prime field element. * @param[in] size - the number of digits to multiply. * @param[in] level - the number of Karatsuba steps to apply. */ static void fp_mul_karat_imp(dv_t c, const fp_t a, const fp_t b, int size, int level) { int i, h, h1; dv_t a1, b1, a0b0, a1b1, t; dig_t carry; /* Compute half the digits of a or b. */ h = size >> 1; h1 = size - h; dv_null(a1); dv_null(b1); dv_null(a0b0); dv_null(a1b1); TRY { /* Allocate the temp variables. */ dv_new(a1); dv_new(b1); dv_new(a0b0); dv_new(a1b1); dv_new(t); dv_zero(a1, h1 + 1); dv_zero(b1, h1 + 1); dv_zero(a0b0, 2 * h); dv_zero(a1b1, 2 * h1); dv_zero(t, 2 * h1 + 1); /* a0b0 = a0 * b0 and a1b1 = a1 * b1 */ if (level <= 1) { #if FP_MUL == BASIC for (i = 0; i < h; i++) { carry = bn_mula_low(a0b0 + i, a, *(b + i), h); *(a0b0 + i + h) = carry; } for (i = 0; i < h1; i++) { carry = bn_mula_low(a1b1 + i, a + h, *(b + h + i), h1); *(a1b1 + i + h1) = carry; } #elif FP_MUL == COMBA || FP_MUL == INTEG bn_muln_low(a0b0, a, b, h); bn_muln_low(a1b1, a + h, b + h, h1); #endif } else { fp_mul_karat_imp(a0b0, a, b, h, level - 1); fp_mul_karat_imp(a1b1, a + h, b + h, h1, level - 1); } for (i = 0; i < 2 * h; i++) { c[i] = a0b0[i]; } for (i = 0; i < 2 * h1 + 1; i++) { c[2 * h + i] = a1b1[i]; } /* a1 = (a1 + a0) */ carry = bn_addn_low(a1, a, a + h, h); bn_add1_low(a1 + h, a1 + h, carry, 2); if (h1 > h) { bn_add1_low(a1 + h, a1 + h, *(a + 2 * h), 2); } /* b1 = (b1 + b0) */ carry = bn_addn_low(b1, b, b + h, h); bn_add1_low(b1 + h, b1 + h, carry, 2); if (h1 > h) { bn_add1_low(b1 + h, b1 + h, *(b + 2 * h), 2); } if (level <= 1) { /* t = (a1 + a0)*(b1 + b0) */ #if FP_MUL == BASIC for (i = 0; i < h1 + 1; i++) { carry = bn_mula_low(t + i, a1, *(b1 + i), h1 + 1); *(t + i + h1 + 1) = carry; } #elif FP_MUL == COMBA || FP_MUL == INTEG bn_muln_low(t, a1, b1, h1 + 1); #endif } else { fp_mul_karat_imp(t, a1, b1, h1 + 1, level - 1); } /* t = t - (a0*b0 << h digits) */ carry = bn_subn_low(t, t, a0b0, 2 * h); bn_sub1_low(t + 2 * h, t + 2 * h, carry, 2 * (h1 + 1) - 2 * h); /* t = t - (a1*b1 << h digits) */ carry = bn_subn_low(t, t, a1b1, 2 * h1); bn_sub1_low(t + 2 * h1, t + 2 * h1, carry, 2 * (h1 + 1) - 2 * h1); /* c = c + [(a1 + a0)*(b1 + b0) << digits] */ c += h; carry = bn_addn_low(c, c, t, 2 * (h1 + 1)); c += 2 * (h1 + 1); bn_add1_low(c, c, carry, 2 * size - h - 2 * (h1 + 1)); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(a1); dv_free(b1); dv_free(a0b0); dv_free(a1b1); dv_free(t); } }
void fp_zero(fp_t a) { dv_zero(a, RLC_FP_DIGS); }