X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/75374b2f8b6f78b865555ba9e5588f46003d5da7..62ef3d44b5dea02c9d1070c4f7da650d1df0e381:/sshbn.c diff --git a/sshbn.c b/sshbn.c index 8d4d703c..2a5d1af2 100644 --- a/sshbn.c +++ b/sshbn.c @@ -3,6 +3,7 @@ */ #include +#include #include #include @@ -133,7 +134,7 @@ static void internal_add_shifted(BignumInt *number, int bshift = shift % BIGNUM_INT_BITS; BignumDblInt addend; - addend = n << bshift; + addend = (BignumDblInt)n << bshift; while (addend) { addend += number[word]; @@ -184,17 +185,36 @@ static void internal_mod(BignumInt *a, int alen, ai1 = a[i + 1]; /* Find q = h:a[i] / m0 */ - DIVMOD_WORD(q, r, h, a[i], m0); - - /* Refine our estimate of q by looking at - h:a[i]:a[i+1] / m0:m1 */ - t = MUL_WORD(m1, q); - if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) { - q--; - t -= m1; - r = (r + m0) & BIGNUM_INT_MASK; /* overflow? */ - if (r >= (BignumDblInt) m0 && - t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--; + if (h >= m0) { + /* + * Special case. + * + * To illustrate it, suppose a BignumInt is 8 bits, and + * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then + * our initial division will be 0xA123 / 0xA1, which + * will give a quotient of 0x100 and a divide overflow. + * However, the invariants in this division algorithm + * are not violated, since the full number A1:23:... is + * _less_ than the quotient prefix A1:B2:... and so the + * following correction loop would have sorted it out. + * + * In this situation we set q to be the largest + * quotient we _can_ stomach (0xFF, of course). + */ + q = BIGNUM_INT_MASK; + } else { + DIVMOD_WORD(q, r, h, a[i], m0); + + /* Refine our estimate of q by looking at + h:a[i]:a[i+1] / m0:m1 */ + t = MUL_WORD(m1, q); + if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) { + q--; + t -= m1; + r = (r + m0) & BIGNUM_INT_MASK; /* overflow? */ + if (r >= (BignumDblInt) m0 && + t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--; + } } /* Subtract q * m from a[i...] */ @@ -226,16 +246,25 @@ static void internal_mod(BignumInt *a, int alen, /* * Compute (base ^ exp) % mod. - * The base MUST be smaller than the modulus. - * The most significant word of mod MUST be non-zero. - * We assume that the result array is the same size as the mod array. */ -Bignum modpow(Bignum base, Bignum exp, Bignum mod) +Bignum modpow(Bignum base_in, Bignum exp, Bignum mod) { BignumInt *a, *b, *n, *m; int mshift; int mlen, i, j; - Bignum result; + Bignum base, result; + + /* + * The most significant word of mod needs to be non-zero. It + * should already be, but let's make sure. + */ + assert(mod[mod[0]] != 0); + + /* + * Make sure the base is smaller than the modulus, by reducing + * it modulo the modulus if not. + */ + base = bigmod(base_in, mod); /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ @@ -331,6 +360,8 @@ Bignum modpow(Bignum base, Bignum exp, Bignum mod) n[i] = 0; sfree(n); + freebn(base); + return result; } @@ -528,19 +559,25 @@ Bignum bignum_from_bytes(const unsigned char *data, int nbytes) /* * Read an ssh1-format bignum from a data buffer. Return the number - * of bytes consumed. + * of bytes consumed, or -1 if there wasn't enough data. */ -int ssh1_read_bignum(const unsigned char *data, Bignum * result) +int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result) { const unsigned char *p = data; int i; int w, b; + if (len < 2) + return -1; + w = 0; for (i = 0; i < 2; i++) w = (w << 8) + *p++; b = (w + 7) / 8; /* bits -> bytes */ + if (len < b+2) + return -1; + if (!result) /* just return length */ return b + 2;