#include "amplify_tommath_private.h" #ifdef AMPLIFY_BN_MP_DIV_C /* LibTomMath, multiple-precision integer library -- Tom St Denis */ /* SPDX-License-Identifier: Unlicense */ /* Modifications Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. */ #ifdef AMPLIFY_BN_MP_DIV_SMALL /* slower bit-bang division... also smaller */ amplify_mp_err amplify_mp_div(const amplify_mp_int *a, const amplify_mp_int *b, amplify_mp_int *c, amplify_mp_int *d) { amplify_mp_int ta, tb, tq, q; int n, n2; amplify_mp_err err; /* is divisor zero ? */ if (AMPLIFY_MP_IS_ZERO(b)) { return AMPLIFY_MP_VAL; } /* if a < b then q=0, r = a */ if (amplify_mp_cmp_mag(a, b) == AMPLIFY_MP_LT) { if (d != NULL) { err = amplify_mp_copy(a, d); } else { err = AMPLIFY_MP_OKAY; } if (c != NULL) { amplify_mp_zero(c); } return err; } /* init our temps */ if ((err = amplify_mp_init_multi(&ta, &tb, &tq, &q, NULL)) != AMPLIFY_MP_OKAY) { return err; } amplify_mp_set(&tq, 1uL); n = amplify_mp_count_bits(a) - amplify_mp_count_bits(b); if ((err = amplify_mp_abs(a, &ta)) != AMPLIFY_MP_OKAY) goto LBL_ERR; if ((err = amplify_mp_abs(b, &tb)) != AMPLIFY_MP_OKAY) goto LBL_ERR; if ((err = amplify_mp_mul_2d(&tb, n, &tb)) != AMPLIFY_MP_OKAY) goto LBL_ERR; if ((err = amplify_mp_mul_2d(&tq, n, &tq)) != AMPLIFY_MP_OKAY) goto LBL_ERR; while (n-- >= 0) { if (amplify_mp_cmp(&tb, &ta) != AMPLIFY_MP_GT) { if ((err = amplify_mp_sub(&ta, &tb, &ta)) != AMPLIFY_MP_OKAY) goto LBL_ERR; if ((err = amplify_mp_add(&q, &tq, &q)) != AMPLIFY_MP_OKAY) goto LBL_ERR; } if ((err = amplify_mp_div_2d(&tb, 1, &tb, NULL)) != AMPLIFY_MP_OKAY) goto LBL_ERR; if ((err = amplify_mp_div_2d(&tq, 1, &tq, NULL)) != AMPLIFY_MP_OKAY) goto LBL_ERR; } /* now q == quotient and ta == remainder */ n = a->sign; n2 = (a->sign == b->sign) ? AMPLIFY_MP_ZPOS : AMPLIFY_MP_NEG; if (c != NULL) { amplify_mp_exch(c, &q); c->sign = AMPLIFY_MP_IS_ZERO(c) ? AMPLIFY_MP_ZPOS : n2; } if (d != NULL) { amplify_mp_exch(d, &ta); d->sign = AMPLIFY_MP_IS_ZERO(d) ? AMPLIFY_MP_ZPOS : n; } LBL_ERR: amplify_mp_clear_multi(&ta, &tb, &tq, &q, NULL); return err; } #else /* integer signed division. * c*b + d == a [e.g. a/b, c=quotient, d=remainder] * HAC pp.598 Algorithm 14.20 * * Note that the description in HAC is horribly * incomplete. For example, it doesn't consider * the case where digits are removed from 'x' in * the inner loop. It also doesn't consider the * case that y has fewer than three digits, etc.. * * The overall algorithm is as described as * 14.20 from HAC but fixed to treat these cases. */ amplify_mp_err amplify_mp_div(const amplify_mp_int *a, const amplify_mp_int *b, amplify_mp_int *c, amplify_mp_int *d) { amplify_mp_int q, x, y, t1, t2; int n, t, i, norm; amplify_mp_sign neg; amplify_mp_err err; /* is divisor zero ? */ if (AMPLIFY_MP_IS_ZERO(b)) { return AMPLIFY_MP_VAL; } /* if a < b then q=0, r = a */ if (amplify_mp_cmp_mag(a, b) == AMPLIFY_MP_LT) { if (d != NULL) { err = amplify_mp_copy(a, d); } else { err = AMPLIFY_MP_OKAY; } if (c != NULL) { amplify_mp_zero(c); } return err; } if ((err = amplify_mp_init_size(&q, a->used + 2)) != AMPLIFY_MP_OKAY) { return err; } q.used = a->used + 2; if ((err = amplify_mp_init(&t1)) != AMPLIFY_MP_OKAY) goto LBL_Q; if ((err = amplify_mp_init(&t2)) != AMPLIFY_MP_OKAY) goto LBL_T1; if ((err = amplify_mp_init_copy(&x, a)) != AMPLIFY_MP_OKAY) goto LBL_T2; if ((err = amplify_mp_init_copy(&y, b)) != AMPLIFY_MP_OKAY) goto LBL_X; /* fix the sign */ neg = (a->sign == b->sign) ? AMPLIFY_MP_ZPOS : AMPLIFY_MP_NEG; x.sign = y.sign = AMPLIFY_MP_ZPOS; /* normalize both x and y, ensure that y >= b/2, [b == 2**AMPLIFY_MP_DIGIT_BIT] */ norm = amplify_mp_count_bits(&y) % AMPLIFY_MP_DIGIT_BIT; if (norm < (AMPLIFY_MP_DIGIT_BIT - 1)) { norm = (AMPLIFY_MP_DIGIT_BIT - 1) - norm; if ((err = amplify_mp_mul_2d(&x, norm, &x)) != AMPLIFY_MP_OKAY) goto LBL_Y; if ((err = amplify_mp_mul_2d(&y, norm, &y)) != AMPLIFY_MP_OKAY) goto LBL_Y; } else { norm = 0; } /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ n = x.used - 1; t = y.used - 1; /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ /* y = y*b**{n-t} */ if ((err = amplify_mp_lshd(&y, n - t)) != AMPLIFY_MP_OKAY) goto LBL_Y; while (amplify_mp_cmp(&x, &y) != AMPLIFY_MP_LT) { ++(q.dp[n - t]); if ((err = amplify_mp_sub(&x, &y, &x)) != AMPLIFY_MP_OKAY) goto LBL_Y; } /* reset y by shifting it back down */ amplify_mp_rshd(&y, n - t); /* step 3. for i from n down to (t + 1) */ for (i = n; i >= (t + 1); i--) { if (i > x.used) { continue; } /* step 3.1 if xi == yt then set q{i-t-1} to b-1, * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ if (x.dp[i] == y.dp[t]) { q.dp[(i - t) - 1] = ((amplify_mp_digit)1 << (amplify_mp_digit)AMPLIFY_MP_DIGIT_BIT) - (amplify_mp_digit)1; } else { amplify_mp_word tmp; tmp = (amplify_mp_word)x.dp[i] << (amplify_mp_word)AMPLIFY_MP_DIGIT_BIT; tmp |= (amplify_mp_word)x.dp[i - 1]; tmp /= (amplify_mp_word)y.dp[t]; if (tmp > (amplify_mp_word)AMPLIFY_MP_MASK) { tmp = AMPLIFY_MP_MASK; } q.dp[(i - t) - 1] = (amplify_mp_digit)(tmp & (amplify_mp_word)AMPLIFY_MP_MASK); } /* while (q{i-t-1} * (yt * b + y{t-1})) > xi * b**2 + xi-1 * b + xi-2 do q{i-t-1} -= 1; */ q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1uL) & (amplify_mp_digit)AMPLIFY_MP_MASK; do { q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1uL) & (amplify_mp_digit)AMPLIFY_MP_MASK; /* find left hand */ amplify_mp_zero(&t1); t1.dp[0] = ((t - 1) < 0) ? 0u : y.dp[t - 1]; t1.dp[1] = y.dp[t]; t1.used = 2; if ((err = amplify_mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != AMPLIFY_MP_OKAY) goto LBL_Y; /* find right hand */ t2.dp[0] = ((i - 2) < 0) ? 0u : x.dp[i - 2]; t2.dp[1] = x.dp[i - 1]; /* i >= 1 always holds */ t2.dp[2] = x.dp[i]; t2.used = 3; } while (amplify_mp_cmp_mag(&t1, &t2) == AMPLIFY_MP_GT); /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ if ((err = amplify_mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != AMPLIFY_MP_OKAY) goto LBL_Y; if ((err = amplify_mp_lshd(&t1, (i - t) - 1)) != AMPLIFY_MP_OKAY) goto LBL_Y; if ((err = amplify_mp_sub(&x, &t1, &x)) != AMPLIFY_MP_OKAY) goto LBL_Y; /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ if (x.sign == AMPLIFY_MP_NEG) { if ((err = amplify_mp_copy(&y, &t1)) != AMPLIFY_MP_OKAY) goto LBL_Y; if ((err = amplify_mp_lshd(&t1, (i - t) - 1)) != AMPLIFY_MP_OKAY) goto LBL_Y; if ((err = amplify_mp_add(&x, &t1, &x)) != AMPLIFY_MP_OKAY) goto LBL_Y; q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1uL) & AMPLIFY_MP_MASK; } } /* now q is the quotient and x is the remainder * [which we have to normalize] */ /* get sign before writing to c */ x.sign = (x.used == 0) ? AMPLIFY_MP_ZPOS : a->sign; if (c != NULL) { amplify_mp_clamp(&q); amplify_mp_exch(&q, c); c->sign = neg; } if (d != NULL) { if ((err = amplify_mp_div_2d(&x, norm, &x, NULL)) != AMPLIFY_MP_OKAY) goto LBL_Y; amplify_mp_exch(&x, d); } err = AMPLIFY_MP_OKAY; LBL_Y: amplify_mp_clear(&y); LBL_X: amplify_mp_clear(&x); LBL_T2: amplify_mp_clear(&t2); LBL_T1: amplify_mp_clear(&t1); LBL_Q: amplify_mp_clear(&q); return err; } #endif #endif