*/
#include <stdio.h>
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define BIGNUM_TOP_BIT 0x80000000UL
#define BIGNUM_INT_BITS 32
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
+#define DIVMOD_WORD(q, r, hi, lo, w) \
+ __asm__("div %2" : \
+ "=d" (r), "=a" (q) : \
+ "r" (w), "d" (hi), "a" (lo))
#else
typedef unsigned short BignumInt;
typedef unsigned long BignumDblInt;
#define BIGNUM_TOP_BIT 0x8000U
#define BIGNUM_INT_BITS 16
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
+#define DIVMOD_WORD(q, r, hi, lo, w) do { \
+ BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
+ q = n / w; \
+ r = n % w; \
+} while (0)
#endif
#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)
int bshift = shift % BIGNUM_INT_BITS;
BignumDblInt addend;
- addend = n << bshift;
+ addend = (BignumDblInt)n << bshift;
while (addend) {
addend += number[word];
ai1 = a[i + 1];
/* Find q = h:a[i] / m0 */
- t = ((BignumDblInt) h << BIGNUM_INT_BITS) + a[i];
- q = t / m0;
- r = t % 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 = (BignumDblInt) m1 * (BignumDblInt) q;
+ t = MUL_WORD(m1, q);
if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) {
q--;
t -= m1;
/* Subtract q * m from a[i...] */
c = 0;
for (k = mlen - 1; k >= 0; k--) {
- t = (BignumDblInt) q * (BignumDblInt) m[k];
+ t = MUL_WORD(q, m[k]);
t += c;
c = t >> BIGNUM_INT_BITS;
if ((BignumInt) t > a[i + k])
/*
* 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 */
n[i] = 0;
sfree(n);
+ freebn(base);
+
return result;
}
/*
* 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;
}
ret[0] = maxspot;
+ sfree(workspace);
return ret;
}
r = 0;
mod = modulus;
for (i = number[0]; i > 0; i--)
- r = (r * 65536 + number[i]) % mod;
+ r = (r * (BIGNUM_TOP_BIT % mod) * 2 + number[i] % mod) % mod;
return (unsigned short) r;
}
x = bigmuladd(q, xp, t);
sign = -sign;
freebn(t);
+ freebn(q);
}
freebn(b);
/*
* Done.
*/
+ sfree(workspace);
return ret;
}