From 43152353176dfadc83a880dcec2f21312bf6fa19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Fri, 15 Nov 2002 08:57:42 +0100
Subject: [PATCH] (nettle_mpz_sizeinbase_256_s): New function.
 (nettle_mpz_sizeinbase_256_u): New name, was nettle_mpz_sizeinbase_256.
 (nettle_mpz_to_octets): New function. (nettle_mpz_get_str_256): Handle
 negative numbers. (nettle_mpz_from_octets): New function.
 (nettle_mpz_set_str_256_u): New name, was nettle_mpz_set_str_256.
 (nettle_mpz_init_set_str_256_u): New name, was nettle_mpz_init_set_str_256.
 (nettle_mpz_set_str_256_s): New function, handling negative two's complement
 numbers. (nettle_mpz_init_set_str_256_s): And an init variant.

Rev: src/nettle/bignum.c:1.5
Rev: src/nettle/bignum.h:1.6
---
 bignum.c | 137 +++++++++++++++++++++++++++++++++++++++++++++----------
 bignum.h |  32 ++++++++++---
 2 files changed, 138 insertions(+), 31 deletions(-)

diff --git a/bignum.c b/bignum.c
index 67abe5d8..d923d702 100644
--- a/bignum.c
+++ b/bignum.c
@@ -34,29 +34,54 @@
 #include <assert.h>
 #include <string.h>
 
+/* Two's complement negation means that -x = ~x + 1, ~x = -(x+1),
+ * and we use that x = ~~x = ~(-x-1).
+ *
+ * Examples:
+ *
+ *   x  ~x = -x+1     ~~x = x
+ *  -1          0          ff
+ *  -2          1          fe
+ * -7f         7e          81
+ * -80         7f          80
+ * -81         80        ff7f
+ */
+
+/* Including extra sign bit, if needed. Also one byte for zero. */
 unsigned
-nettle_mpz_sizeinbase_256(const mpz_t x)
+nettle_mpz_sizeinbase_256_s(const mpz_t x)
 {
-  return (mpz_sizeinbase(x, 2) + 7) / 8;
+  if (mpz_sgn(x) >= 0)
+    return 1 + mpz_sizeinbase(x, 2) / 8;
+  else
+    {
+      /* We'll output ~~x, so we need as many bits as for ~x */
+      unsigned size;
+      mpz_t c;
+
+      mpz_init(c);
+      mpz_com(c, x); /* Same as c = - x - 1 = |x| + 1 */
+      size = 1 + mpz_sizeinbase(c,2) / 8;
+      mpz_clear(c);
+
+      return size;
+    }
 }
 
-void
-nettle_mpz_get_str_256(unsigned length, uint8_t *s, const mpz_t x)
+unsigned
+nettle_mpz_sizeinbase_256_u(const mpz_t x)
+{
+  return (mpz_sizeinbase(x,2) + 7) / 8;
+}
+
+static void
+nettle_mpz_to_octets(unsigned length, uint8_t *s,
+		     const mpz_t x, uint8_t sign)
 {
   uint8_t *dst = s + length - 1;
   unsigned size = mpz_size(x);
   unsigned i;
-
-  if (!length)
-    {
-      /* x must be zero */
-      assert(!mpz_sgn(x));
-      return;
-    }
   
-  assert(mpz_sgn(x) >= 0);
-  assert(nettle_mpz_sizeinbase_256(x) <= length);
-
   for (i = 0; i<size; i++)
     {
       mp_limb_t limb = mpz_getlimbn(x, i);
@@ -64,28 +89,58 @@ nettle_mpz_get_str_256(unsigned length, uint8_t *s, const mpz_t x)
 
       for (j = 0; length && j < sizeof(mp_limb_t); j++)
         {
-          *dst-- = limb & 0xff;
+          *dst-- = sign ^ (limb & 0xff);
           limb >>= 8;
           length--;
 	}
     }
   
   if (length)
-    memset(s, 0, length);
+    memset(s, sign, length);
 }
 
 void
-nettle_mpz_set_str_256(mpz_t x,
-                       unsigned length, const uint8_t *s)
+nettle_mpz_get_str_256(unsigned length, uint8_t *s, const mpz_t x)
+{
+  if (!length)
+    {
+      /* x must be zero */
+      assert(!mpz_sgn(x));
+      return;
+    }
+
+  if (mpz_sgn(x) >= 0)
+    {
+      assert(nettle_mpz_sizeinbase_256_u(x) <= length);
+      nettle_mpz_to_octets(length, s, x, 0);
+    }
+  else
+    {
+      mpz_t c;
+      mpz_init(c);
+      mpz_com(c, x);
+      
+      assert(nettle_mpz_sizeinbase_256_u(c) <= length);
+      nettle_mpz_to_octets(length, s, c, 0xff);
+
+      mpz_clear(c);
+    }
+}
+
+/* Converting from strings */
+static void
+nettle_mpz_from_octets(mpz_t x,
+		       unsigned length, const uint8_t *s,
+		       uint8_t sign)
 {
-  size_t i;
+  unsigned i;
   mpz_t digit;
 
   mpz_init(digit);
-  mpz_set_ui(x, 0);
+
   for (i = 0; i < length; i++)
     {
-      mpz_set_ui(digit, s[i]);
+      mpz_set_ui(digit, sign ^ s[i]);
       mpz_mul_2exp(digit, digit, (length - i - 1) * 8);
       mpz_ior(x, x, digit);
     }
@@ -93,11 +148,45 @@ nettle_mpz_set_str_256(mpz_t x,
 }
 
 void
-nettle_mpz_init_set_str_256(mpz_t x,
-                            unsigned length, const uint8_t *s)
+nettle_mpz_set_str_256_u(mpz_t x,
+			 unsigned length, const uint8_t *s)
+{
+  mpz_set_ui(x, 0);
+  nettle_mpz_from_octets(x, length, s, 0);
+}
+
+void
+nettle_mpz_init_set_str_256_u(mpz_t x,
+			      unsigned length, const uint8_t *s)
+{
+  mpz_init_set_ui(x, 0);
+  nettle_mpz_from_octets(x, length, s, 0);
+}
+
+void
+nettle_mpz_set_str_256_s(mpz_t x,
+			 unsigned length, const uint8_t *s)
+{
+  mpz_set_ui(x, 0);
+
+  if (!length)
+    return;
+  
+  if (s[0] & 0x80)
+    {
+      nettle_mpz_from_octets(x, length, s, 0xff);
+      mpz_com(x, x);
+    }
+  else
+    nettle_mpz_from_octets(x, length, s, 0);
+}
+
+void
+nettle_mpz_init_set_str_256_s(mpz_t x,
+			      unsigned length, const uint8_t *s)
 {
   mpz_init(x);
-  nettle_mpz_set_str_256(x, length, s);
+  nettle_mpz_set_str_256_s(x, length, s);
 }
 
 #endif /* HAVE_LIBGMP */
diff --git a/bignum.h b/bignum.h
index f4c9ed07..3ba8ea5d 100644
--- a/bignum.h
+++ b/bignum.h
@@ -31,21 +31,39 @@
 #include <gmp.h>
 #include <inttypes.h>
 
+/* Size needed for signed encoding, including extra sign byte if
+ * necessary. */
 unsigned
-nettle_mpz_sizeinbase_256(const mpz_t x);
+nettle_mpz_sizeinbase_256_s(const mpz_t x);
 
-/* Writes an unsigned integer as length octets, using big endian byte
- * order. */
+/* Size needed for unsigned encoding */
+unsigned
+nettle_mpz_sizeinbase_256_u(const mpz_t x);
+
+/* Writes an integer as length octets, using big endian byte order,
+ * and two's complement for negative numbers. */
+/* FIXME: Change order of arguments, putting the mpz_t first? */
 void
 nettle_mpz_get_str_256(unsigned length, uint8_t *s, const mpz_t x);
 
+/* Reads a big endian, two's complement, integer. */
+void
+nettle_mpz_set_str_256_s(mpz_t x,
+			 unsigned length, const uint8_t *s);
+
+void
+nettle_mpz_init_set_str_256_s(mpz_t x,
+			      unsigned length, const uint8_t *s);
+
+/* Similar, but for unsigned format. These function don't interpret
+ * the most significant bit as the sign. */
 void
-nettle_mpz_set_str_256(mpz_t x,
-                       unsigned length, const uint8_t *s);
+nettle_mpz_set_str_256_u(mpz_t x,
+			 unsigned length, const uint8_t *s);
 
 void
-nettle_mpz_init_set_str_256(mpz_t x,
-                            unsigned length, const uint8_t *s);
+nettle_mpz_init_set_str_256_u(mpz_t x,
+			      unsigned length, const uint8_t *s);
 
 /* Returns a uniformly distributed random number 0 <= x < 2^n */
 void
-- 
GitLab