summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--doc/gcrypt.texi15
-rw-r--r--mpi/mpicoder.c137
-rw-r--r--tests/t-convert.c374
4 files changed, 495 insertions, 34 deletions
diff --git a/NEWS b/NEWS
index e5ea856d..46b52977 100644
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,9 @@ Noteworthy changes in version 1.6.0 (unreleased)
* Added several MPI helper functions.
+ * Added support for negative numbers to gcry_mpi_print,
+ gcry_mpi_aprint and gcry_mpi_scan.
+
* Interface changes relative to the 1.5.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcry_ac_* REMOVED.
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index afef7f76..63fd5963 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -3826,7 +3826,8 @@ bytes actually scanned unless @var{nscanned} was given as
@table @code
@item GCRYMPI_FMT_STD
-2-complement stored without a length header.
+2-complement stored without a length header. Note that
+@code{gcry_mpi_print} stores a @code{0} as a string of zero length.
@item GCRYMPI_FMT_PGP
As used by OpenPGP (only defined as unsigned). This is basically
@@ -3837,8 +3838,10 @@ As used in the Secure Shell protocol. This is @code{GCRYMPI_FMT_STD}
with a 4 byte big endian header.
@item GCRYMPI_FMT_HEX
-Stored as a C style string with each byte of the MPI encoded as 2 hex
-digits. When using this format, @var{buflen} must be zero.
+Stored as a string with each byte of the MPI encoded as 2 hex digits.
+Negative numbers are prefix with a minus sign and in addition the
+high bit is always zero to make clear that an explicit sign ist used.
+When using this format, @var{buflen} must be zero.
@item GCRYMPI_FMT_USG
Simple unsigned integer.
@@ -3866,6 +3869,12 @@ Convert the MPI @var{a} into an external representation described by
address will be stored in the variable @var{buffer} points to. The
number of bytes stored in this buffer will be stored in the variable
@var{nbytes} points to, unless @var{nbytes} is @code{NULL}.
+
+Even if @var{nbytes} is zero, the function allocates at least one byte
+and store a zero there. Thus with formats @code{GCRYMPI_FMT_STD} and
+@code{GCRYMPI_FMT_USG} the caller may safely set a returned length of
+0 to 1 to represent a zero as a 1 byte string.
+
@end deftypefun
@deftypefun void gcry_mpi_dump (@w{const gcry_mpi_t @var{a}})
diff --git a/mpi/mpicoder.c b/mpi/mpicoder.c
index 19c8de90..07e91c63 100644
--- a/mpi/mpicoder.c
+++ b/mpi/mpicoder.c
@@ -353,6 +353,65 @@ _gcry_mpi_set_buffer (gcry_mpi_t a, const void *buffer_arg,
}
+static void
+onecompl (gcry_mpi_t a)
+{
+ mpi_ptr_t ap;
+ mpi_size_t n;
+ unsigned int i;
+ unsigned int nbits = mpi_get_nbits (a);
+
+ if (mpi_is_immutable (a))
+ {
+ mpi_immutable_failed ();
+ return;
+ }
+
+ mpi_normalize (a);
+ ap = a->d;
+ n = a->nlimbs;
+
+ for( i = 0; i < n; i++ )
+ ap[i] ^= (mpi_limb_t)(-1);
+
+ a->sign = 0;
+ mpi_clear_highbit (a, nbits-1);
+}
+
+
+/* Perform a two's complement operation on buffer P of size N bytes. */
+static void
+twocompl (unsigned char *p, unsigned int n)
+{
+ int i;
+
+ for (i=n-1; i >= 0 && !p[i]; i--)
+ ;
+ if (i >= 0)
+ {
+ if ((p[i] & 0x01))
+ p[i] = (((p[i] ^ 0xfe) | 0x01) & 0xff);
+ else if ((p[i] & 0x02))
+ p[i] = (((p[i] ^ 0xfc) | 0x02) & 0xfe);
+ else if ((p[i] & 0x04))
+ p[i] = (((p[i] ^ 0xf8) | 0x04) & 0xfc);
+ else if ((p[i] & 0x08))
+ p[i] = (((p[i] ^ 0xf0) | 0x08) & 0xf8);
+ else if ((p[i] & 0x10))
+ p[i] = (((p[i] ^ 0xe0) | 0x10) & 0xf0);
+ else if ((p[i] & 0x20))
+ p[i] = (((p[i] ^ 0xc0) | 0x20) & 0xe0);
+ else if ((p[i] & 0x40))
+ p[i] = (((p[i] ^ 0x80) | 0x40) & 0xc0);
+ else
+ p[i] = 0x80;
+
+ for (i--; i >= 0; i--)
+ p[i] ^= 0xff;
+ }
+}
+
+
/* Convert the external representation of an integer stored in BUFFER
with a length of BUFLEN into a newly create MPI returned in
RET_MPI. If NBYTES is not NULL, it will receive the number of
@@ -380,15 +439,14 @@ gcry_mpi_scan (struct gcry_mpi **ret_mpi, enum gcry_mpi_format format,
: mpi_alloc ((len+BYTES_PER_MPI_LIMB-1)/BYTES_PER_MPI_LIMB);
if (len)
{
+ _gcry_mpi_set_buffer (a, s, len, 0);
a->sign = !!(*s & 0x80);
if (a->sign)
{
- /* FIXME: we have to convert from 2compl to magnitude format */
- mpi_free (a);
- return gcry_error (GPG_ERR_INTERNAL);
+ onecompl (a);
+ mpi_add_ui (a, a, 1);
+ a->sign = 1;
}
- else
- _gcry_mpi_set_buffer (a, s, len, 0);
}
if (ret_mpi)
{
@@ -397,6 +455,8 @@ gcry_mpi_scan (struct gcry_mpi **ret_mpi, enum gcry_mpi_format format,
}
else
mpi_free(a);
+ if (nscanned)
+ *nscanned = len;
return 0;
}
else if (format == GCRYMPI_FMT_USG)
@@ -414,6 +474,8 @@ gcry_mpi_scan (struct gcry_mpi **ret_mpi, enum gcry_mpi_format format,
}
else
mpi_free(a);
+ if (nscanned)
+ *nscanned = len;
return 0;
}
else if (format == GCRYMPI_FMT_PGP)
@@ -458,14 +520,13 @@ gcry_mpi_scan (struct gcry_mpi **ret_mpi, enum gcry_mpi_format format,
if (n)
{
a->sign = !!(*s & 0x80);
+ _gcry_mpi_set_buffer( a, s, n, 0 );
if (a->sign)
{
- /* FIXME: we have to convert from 2compl to magnitude format */
- mpi_free(a);
- return gcry_error (GPG_ERR_INTERNAL);
+ onecompl (a);
+ mpi_add_ui (a, a, 1);
+ a->sign = 1;
}
- else
- _gcry_mpi_set_buffer( a, s, n, 0 );
}
if (nscanned)
*nscanned = n+4;
@@ -497,6 +558,8 @@ gcry_mpi_scan (struct gcry_mpi **ret_mpi, enum gcry_mpi_format format,
}
else
mpi_free(a);
+ if (nscanned)
+ *nscanned = strlen ((const char*)buffer);
return 0;
}
else
@@ -541,18 +604,25 @@ gcry_mpi_print (enum gcry_mpi_format format,
int extra = 0;
unsigned int n;
- if (negative)
- return gcry_error (GPG_ERR_INTERNAL); /* Can't handle it yet. */
-
tmp = _gcry_mpi_get_buffer (a, 0, &n, NULL);
if (!tmp)
return gpg_error_from_syserror ();
- /* If the high bit of the returned buffer is set, we need to
- print an extra leading 0x00 so that the output is interpreted
- as a positive number. */
- if (n && (*tmp & 0x80))
+ if (negative)
{
+ twocompl (tmp, n);
+ if (!(*tmp & 0x80))
+ {
+ /* Need to extend the sign. */
+ n++;
+ extra = 2;
+ }
+ }
+ else if (n && (*tmp & 0x80))
+ {
+ /* Positive but the high bit of the returned buffer is set.
+ Thus we need to print an extra leading 0x00 so that the
+ output is interpreted as a positive number. */
n++;
extra = 1;
}
@@ -567,9 +637,11 @@ gcry_mpi_print (enum gcry_mpi_format format,
{
unsigned char *s = buffer;
- if (extra)
+ if (extra == 1)
*s++ = 0;
- memcpy (s, tmp, n-extra);
+ else if (extra)
+ *s++ = 0xff;
+ memcpy (s, tmp, n-!!extra);
}
gcry_free(tmp);
*nwritten = n;
@@ -632,13 +704,21 @@ gcry_mpi_print (enum gcry_mpi_format format,
int extra = 0;
unsigned int n;
- if (negative)
- return gcry_error (GPG_ERR_INTERNAL); /* Can't handle it yet. */
-
tmp = _gcry_mpi_get_buffer (a, 0, &n, NULL);
if (!tmp)
return gpg_error_from_syserror ();
- if (n && (*tmp & 0x80))
+
+ if (negative)
+ {
+ twocompl (tmp, n);
+ if (!(*tmp & 0x80))
+ {
+ /* Need to extend the sign. */
+ n++;
+ extra = 2;
+ }
+ }
+ else if (n && (*tmp & 0x80))
{
n++;
extra=1;
@@ -658,10 +738,11 @@ gcry_mpi_print (enum gcry_mpi_format format,
*s++ = n >> 16;
*s++ = n >> 8;
*s++ = n;
- if (extra)
+ if (extra == 1)
*s++ = 0;
-
- memcpy (s, tmp, n-extra);
+ else if (extra)
+ *s++ = 0xff;
+ memcpy (s, tmp, n-!!extra);
}
gcry_free (tmp);
*nwritten = 4+n;
@@ -741,6 +822,10 @@ gcry_mpi_aprint (enum gcry_mpi_format format,
*buffer = mpi_is_secure(a) ? gcry_malloc_secure (n?n:1) : gcry_malloc (n?n:1);
if (!*buffer)
return gpg_error_from_syserror ();
+ /* If the returned buffer will have a length of 0, we nevertheless
+ allocated 1 byte (malloc needs it anyway) and store a 0. */
+ if (!n)
+ **buffer = 0;
rc = gcry_mpi_print( format, *buffer, n, &n, a );
if (rc)
{
diff --git a/tests/t-convert.c b/tests/t-convert.c
index b7f97fa3..d44c4395 100644
--- a/tests/t-convert.c
+++ b/tests/t-convert.c
@@ -28,7 +28,10 @@
#include "../src/gcrypt-int.h"
-#define PGM "t-mpi-point"
+#define PGM "t-convert"
+
+#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+#define DIMof(type,member) DIM(((type *)0)->member)
static const char *wherestr;
static int verbose;
@@ -55,6 +58,61 @@ show (const char *format, ...)
}
static void
+showhex (const char *prefix, const void *buffer, size_t buflen)
+{
+ const unsigned char *s;
+
+ if (!verbose)
+ return;
+ fprintf (stderr, "%s: %s ", PGM, prefix);
+ for (s= buffer; buflen; buflen--, s++)
+ fprintf (stderr, "%02x", *s);
+ putc ('\n', stderr);
+}
+
+
+/* Allocate a bit string consisting of '0' and '1' from the MPI A. Do
+ not return any leading zero bits. Caller needs to gcry_free the
+ result. */
+static char *
+mpi2bitstr_nlz (gcry_mpi_t a)
+{
+ char *p, *buf;
+ size_t length = gcry_mpi_get_nbits (a);
+
+ if (!length)
+ {
+ buf = p = xmalloc (3);
+ *p++ = ' ';
+ *p++ = '0';
+ }
+ else
+ {
+ buf = p = xmalloc (length + 1 + 1);
+ *p++ = gcry_mpi_is_neg (a)? '-':' ';
+ while (length-- > 1)
+ *p++ = gcry_mpi_test_bit (a, length) ? '1':'0';
+ *p++ = gcry_mpi_test_bit (a, 0) ? '1':'0';
+ }
+ *p = 0;
+ return buf;
+}
+
+
+static void
+showmpi (const char *prefix, gcry_mpi_t a)
+{
+ char *bitstr;
+
+ if (!verbose)
+ return;
+ bitstr = mpi2bitstr_nlz (a);
+ fprintf (stderr, "%s: %s%s\n", PGM, prefix, bitstr);
+ xfree (bitstr);
+}
+
+
+static void
fail (const char *format, ...)
{
va_list arg_ptr;
@@ -95,10 +153,10 @@ negative_zero (void)
void *bufaddr = &buf;
struct { const char *name; enum gcry_mpi_format format; } fmts[] =
{
- { "STD", GCRYMPI_FMT_STD },
- { "PGP", GCRYMPI_FMT_PGP },
- { "SSH", GCRYMPI_FMT_SSH },
- { "HEX", GCRYMPI_FMT_HEX },
+ /* { "STD", GCRYMPI_FMT_STD }, */
+ /* { "PGP", GCRYMPI_FMT_PGP }, */
+ /* { "SSH", GCRYMPI_FMT_SSH }, */
+ /* { "HEX", GCRYMPI_FMT_HEX }, */
{ "USG", GCRYMPI_FMT_USG },
{ NULL, 0 }
};
@@ -134,6 +192,311 @@ negative_zero (void)
}
+static void
+check_formats (void)
+{
+ static struct {
+ int value;
+ struct {
+ const char *hex;
+ size_t stdlen;
+ const char *std;
+ size_t sshlen;
+ const char *ssh;
+ size_t usglen;
+ const char *usg;
+ } a;
+ } data[] = {
+ { 0, { "00",
+ 0, "",
+ 4, "\x00\x00\x00\x00",
+ 0, "" }
+ },
+ { 1, { "01",
+ 1, "\x01",
+ 5, "\x00\x00\x00\x01\x01",
+ 1, "\x01" }
+ },
+ { 2, { "02",
+ 1, "\x02",
+ 5, "\x00\x00\x00\x01\x02",
+ 1, "\x02", }
+ },
+ { 127, { "7F",
+ 1, "\x7f",
+ 5, "\x00\x00\x00\x01\x7f",
+ 1, "\x7f" }
+ },
+ { 128, { "0080",
+ 2, "\x00\x80",
+ 6, "\x00\x00\x00\x02\x00\x80",
+ 1, "\x80" }
+ },
+ { 129, { "0081",
+ 2, "\x00\x81",
+ 6, "\x00\x00\x00\x02\x00\x81",
+ 1, "\x81" }
+ },
+ { 255, { "00FF",
+ 2, "\x00\xff",
+ 6, "\x00\x00\x00\x02\x00\xff",
+ 1, "\xff" }
+ },
+ { 256, { "0100",
+ 2, "\x01\x00",
+ 6, "\x00\x00\x00\x02\x01\x00",
+ 2, "\x01\x00" }
+ },
+ { 257, { "0101",
+ 2, "\x01\x01",
+ 6, "\x00\x00\x00\x02\x01\x01",
+ 2, "\x01\x01" }
+ },
+ { -1, { "-01",
+ 1, "\xff",
+ 5, "\x00\x00\x00\x01\xff",
+ 1,"\x01" }
+ },
+ { -2, { "-02",
+ 1, "\xfe",
+ 5, "\x00\x00\x00\x01\xfe",
+ 1, "\x02" }
+ },
+ { -127, { "-7F",
+ 1, "\x81",
+ 5, "\x00\x00\x00\x01\x81",
+ 1, "\x7f" }
+ },
+ { -128, { "-0080",
+ 1, "\x80",
+ 5, "\x00\x00\x00\x01\x80",
+ 1, "\x80" }
+ },
+ { -129, { "-0081",
+ 2, "\xff\x7f",
+ 6, "\x00\x00\x00\x02\xff\x7f",
+ 1, "\x81" }
+ },
+ { -255, { "-00FF",
+ 2, "\xff\x01",
+ 6, "\x00\x00\x00\x02\xff\x01",
+ 1, "\xff" }
+ },
+ { -256, { "-0100",
+ 2, "\xff\x00",
+ 6, "\x00\x00\x00\x02\xff\x00",
+ 2, "\x01\x00" }
+ },
+ { -257, { "-0101",
+ 2, "\xfe\xff",
+ 6, "\x00\x00\x00\x02\xfe\xff",
+ 2, "\x01\x01" }
+ },
+ { 65535, { "00FFFF",
+ 3, "\x00\xff\xff",
+ 7, "\x00\x00\x00\x03\x00\xff\xff",
+ 2, "\xff\xff" }
+ },
+ { 65536, { "010000",
+ 3, "\x01\00\x00",
+ 7, "\x00\x00\x00\x03\x01\x00\x00",
+ 3, "\x01\x00\x00" }
+ },
+ { 65537, { "010001",
+ 3, "\x01\00\x01",
+ 7, "\x00\x00\x00\x03\x01\x00\x01",
+ 3, "\x01\x00\x01" }
+ },
+ { -65537, { "-010001",
+ 3, "\xfe\xff\xff",
+ 7, "\x00\x00\x00\x03\xfe\xff\xff",
+ 3, "\x01\x00\x01" }
+ },
+ { -65536, { "-010000",
+ 3, "\xff\x00\x00",
+ 7, "\x00\x00\x00\x03\xff\x00\x00",
+ 3, "\x01\x00\x00" }
+ },
+ { -65535, { "-00FFFF",
+ 3, "\xff\x00\x01",
+ 7, "\x00\x00\x00\x03\xff\x00\x01",
+ 2, "\xff\xff" }
+ }
+ };
+ gpg_error_t err;
+ gcry_mpi_t a, b;
+ char *buf;
+ void *bufaddr = &buf;
+ int idx;
+ size_t buflen;
+
+ a = gcry_mpi_new (0);
+ for (idx=0; idx < DIM(data); idx++)
+ {
+ if (debug)
+ show ("print test %d\n", data[idx].value);
+
+ if (data[idx].value < 0)
+ {
+ gcry_mpi_set_ui (a, -data[idx].value);
+ gcry_mpi_neg (a, a);
+ }
+ else
+ gcry_mpi_set_ui (a, data[idx].value);
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
+ if (err)
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "HEX", gpg_strerror (err));
+ else
+ {
+ if (strcmp (buf, data[idx].a.hex))
+ {
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "HEX", "wrong result");
+ show ("expected: '%s'\n", data[idx].a.hex);
+ show (" got: '%s'\n", buf);
+ }
+ gcry_free (buf);
+ }
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, bufaddr, &buflen, a);
+ if (err)
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "STD", gpg_strerror (err));
+ else
+ {
+ if (buflen != data[idx].a.stdlen
+ || memcmp (buf, data[idx].a.std, data[idx].a.stdlen))
+ {
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "STD", "wrong result");
+ showhex ("expected:", data[idx].a.std, data[idx].a.stdlen);
+ showhex (" got:", buf, buflen);
+ }
+ gcry_free (buf);
+ }
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, bufaddr, &buflen, a);
+ if (err)
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "SSH", gpg_strerror (err));
+ else
+ {
+ if (buflen != data[idx].a.sshlen
+ || memcmp (buf, data[idx].a.ssh, data[idx].a.sshlen))
+ {
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "SSH", "wrong result");
+ showhex ("expected:", data[idx].a.ssh, data[idx].a.sshlen);
+ showhex (" got:", buf, buflen);
+ }
+ gcry_free (buf);
+ }
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufaddr, &buflen, a);
+ if (err)
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "USG", gpg_strerror (err));
+ else
+ {
+ if (buflen != data[idx].a.usglen
+ || memcmp (buf, data[idx].a.usg, data[idx].a.usglen))
+ {
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "USG", "wrong result");
+ showhex ("expected:", data[idx].a.usg, data[idx].a.usglen);
+ showhex (" got:", buf, buflen);
+ }
+ gcry_free (buf);
+ }
+ }
+
+
+ /* Now for the other direction. */
+ for (idx=0; idx < DIM(data); idx++)
+ {
+ if (debug)
+ show ("scan test %d\n", data[idx].value);
+
+ if (data[idx].value < 0)
+ {
+ gcry_mpi_set_ui (a, -data[idx].value);
+ gcry_mpi_neg (a, a);
+ }
+ else
+ gcry_mpi_set_ui (a, data[idx].value);
+
+ err = gcry_mpi_scan (&b, GCRYMPI_FMT_HEX, data[idx].a.hex, 0, &buflen);
+ if (err)
+ fail ("error scanning value %d from %s: %s\n",
+ data[idx].value, "HEX", gpg_strerror (err));
+ else
+ {
+ if (gcry_mpi_cmp (a, b))
+ {
+ fail ("error scanning value %d from %s: %s\n",
+ data[idx].value, "HEX", "wrong result");
+ showmpi ("expected:", a);
+ showmpi (" got:", b);
+ }
+ gcry_mpi_release (b);
+ }
+
+ err = gcry_mpi_scan (&b, GCRYMPI_FMT_STD,
+ data[idx].a.std, data[idx].a.stdlen, &buflen);
+ if (err)
+ fail ("error scanning value %d as %s: %s\n",
+ data[idx].value, "STD", gpg_strerror (err));
+ else
+ {
+ if (gcry_mpi_cmp (a, b) || data[idx].a.stdlen != buflen)
+ {
+ fail ("error scanning value %d from %s: %s (%u)\n",
+ data[idx].value, "STD", "wrong result", buflen);
+ showmpi ("expected:", a);
+ showmpi (" got:", b);
+ }
+ gcry_mpi_release (b);
+ }
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, bufaddr, &buflen, a);
+ if (err)
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "SSH", gpg_strerror (err));
+ else
+ {
+ if (buflen != data[idx].a.sshlen
+ || memcmp (buf, data[idx].a.ssh, data[idx].a.sshlen))
+ {
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "SSH", "wrong result");
+ showhex ("expected:", data[idx].a.ssh, data[idx].a.sshlen);
+ showhex (" got:", buf, buflen);
+ }
+ gcry_free (buf);
+ }
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufaddr, &buflen, a);
+ if (err)
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "USG", gpg_strerror (err));
+ else
+ {
+ if (buflen != data[idx].a.usglen
+ || memcmp (buf, data[idx].a.usg, data[idx].a.usglen))
+ {
+ fail ("error printing value %d as %s: %s\n",
+ data[idx].value, "USG", "wrong result");
+ showhex ("expected:", data[idx].a.usg, data[idx].a.usglen);
+ showhex (" got:", buf, buflen);
+ }
+ gcry_free (buf);
+ }
+ }
+
+ gcry_mpi_release (a);
+}
int
@@ -154,6 +517,7 @@ main (int argc, char **argv)
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
negative_zero ();
+ check_formats ();
show ("All tests completed. Errors: %d\n", error_count);
return error_count ? 1 : 0;