summaryrefslogtreecommitdiff
path: root/mpi/mpicoder.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2013-09-07 10:06:46 +0200
committerWerner Koch <wk@gnupg.org>2013-09-17 15:22:15 +0200
commit89fe2173649a72019d75e059e6c6938efd10421f (patch)
tree2dca440a3424ed505d7015cdaf530f873b5b6467 /mpi/mpicoder.c
parenta7a9cdcaaf3979baa18dad51e722882581349f45 (diff)
downloadlibgcrypt-89fe2173649a72019d75e059e6c6938efd10421f.tar.gz
mpi: Support printing of negative numbers.
* mpi/mpicoder.c (twocompl, onecompl): New. (gcry_mpi_print): Use it for STD and SSH. (gcry_mpi_scan): Use it for STD and SSH. Always set NSCANNED. (gcry_mpi_aprint): Clear the extra allocated byte. * tests/t-convert.c (showhex, showmpi): New. (mpi2bitstr_nlz): New. (check_formats): New. (main): Call new test. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'mpi/mpicoder.c')
-rw-r--r--mpi/mpicoder.c137
1 files changed, 111 insertions, 26 deletions
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)
{