work in progress; to be tidied and fixed
[u/mdw/putty] / sshbn.c
diff --git a/sshbn.c b/sshbn.c
index 0313071..580dd6f 100644 (file)
--- a/sshbn.c
+++ b/sshbn.c
@@ -8,95 +8,7 @@
 #include <string.h>
 
 #include "misc.h"
-
-/*
- * Usage notes:
- *  * Do not call the DIVMOD_WORD macro with expressions such as array
- *    subscripts, as some implementations object to this (see below).
- *  * Note that none of the division methods below will cope if the
- *    quotient won't fit into BIGNUM_INT_BITS. Callers should be careful
- *    to avoid this case.
- *    If this condition occurs, in the case of the x86 DIV instruction,
- *    an overflow exception will occur, which (according to a correspondent)
- *    will manifest on Windows as something like
- *      0xC0000095: Integer overflow
- *    The C variant won't give the right answer, either.
- */
-
-#if defined __GNUC__ && defined __i386__
-typedef unsigned long BignumInt;
-typedef unsigned long long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFUL
-#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))
-#elif defined _MSC_VER && defined _M_IX86
-typedef unsigned __int32 BignumInt;
-typedef unsigned __int64 BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFUL
-#define BIGNUM_TOP_BIT   0x80000000UL
-#define BIGNUM_INT_BITS  32
-#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
-/* Note: MASM interprets array subscripts in the macro arguments as
- * assembler syntax, which gives the wrong answer. Don't supply them.
- * <http://msdn2.microsoft.com/en-us/library/bf1dw62z.aspx> */
-#define DIVMOD_WORD(q, r, hi, lo, w) do { \
-    __asm mov edx, hi \
-    __asm mov eax, lo \
-    __asm div w \
-    __asm mov r, edx \
-    __asm mov q, eax \
-} while(0)
-#elif defined _LP64
-/* 64-bit architectures can do 32x32->64 chunks at a time */
-typedef unsigned int BignumInt;
-typedef unsigned long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFU
-#define BIGNUM_TOP_BIT   0x80000000U
-#define BIGNUM_INT_BITS  32
-#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)
-#elif defined _LLP64
-/* 64-bit architectures in which unsigned long is 32 bits, not 64 */
-typedef unsigned long BignumInt;
-typedef unsigned long long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFUL
-#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) do { \
-    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
-    q = n / w; \
-    r = n % w; \
-} while (0)
-#else
-/* Fallback for all other cases */
-typedef unsigned short BignumInt;
-typedef unsigned long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFU
-#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)
-
-#define BIGNUM_INTERNAL
-typedef BignumInt *Bignum;
-
+#include "bn-internal.h"
 #include "ssh.h"
 
 BignumInt bnZero[1] = { 0 };
@@ -1473,6 +1385,78 @@ Bignum bigsub(Bignum a, Bignum b)
 }
 
 /*
+ * Return a bignum which is the result of shifting another left by N bits.
+ * If N is negative then you get a right shift instead.
+ */
+Bignum biglsl(Bignum x, int n)
+{
+    Bignum d;
+    unsigned o, i;
+
+    if (!n || !x[0])
+        return copybn(x);
+    else if (n < 0)
+        return biglsr(x, -n);
+
+    o = n/BIGNUM_INT_BITS;
+    n %= BIGNUM_INT_BITS;
+    d = newbn(x[0] + o + !!n);
+
+    for (i = 1; i <= o; i++)
+        d[i] = 0;
+
+    if (!n) {
+        for (i = 1; i <= x[0]; i++)
+            d[o + i] = x[i];
+    } else {
+        d[o + 1] = x[1] << n;
+        for (i = 2; i <= x[0]; i--)
+            d[o + i] = (x[i] << n) | (x[i - 1] >> (BIGNUM_INT_BITS - n));
+        d[o + x[0] + 1] = x[x[0]] >> (BIGNUM_INT_BITS - n);
+    }
+
+    bn_restore_invariant(d);
+    return d;
+}
+
+/*
+ * Return a bignum which is the result of shifting another right by N bits
+ * (discarding the least significant N bits, and shifting zeroes in at the
+ * most significant end).  If N is negative then you get a left shift
+ * instead.
+ */
+Bignum biglsr(Bignum x, int n)
+{
+    Bignum d;
+    unsigned o, i;
+
+    if (!n || !x[0])
+        return copybn(x);
+    else if (n < 0)
+        return biglsl(x, -n);
+
+    o = n/BIGNUM_INT_BITS;
+    n %= BIGNUM_INT_BITS;
+    d = newbn(x[0]);
+
+    if (!n) {
+        for (i = o + 1; i <= x[0]; i++)
+            d[i - o] = x[i];
+    } else {
+        d[1] = x[o + 1] >> n;
+        for (i = o + 2; i < x[0]; i++)
+            d[i - o] = x[
+        d[o + x[0] + 1] = x[x[0]] >> (BIGNUM_INT_BITS - n);
+        for (i = x[0]; i > 1; i--)
+            d[o + i] = (x[i] << n) | (x[i - 1] >> (BIGNUM_INT_BITS - n));
+        d[o + 1] = x[1] << n;
+    }
+
+    bn_restore_invariant(d);
+    return d;
+}
+
+/*
  * Create a bignum which is the bitmask covering another one. That
  * is, the smallest integer which is >= N and is also one less than
  * a power of two.