Fixed gcry_mpi_set_bit and enhanced mpi_rshift.
authorWerner Koch <wk@gnupg.org>
Fri, 4 Aug 2006 10:18:14 +0000 (10:18 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 4 Aug 2006 10:18:14 +0000 (10:18 +0000)
Cleaned up andom-daemon initialization.

12 files changed:
NEWS
TODO
cipher/ChangeLog
cipher/random-daemon.c
cipher/random.c
cipher/random.h
mpi/ChangeLog
mpi/mpi-bit.c
mpi/mpiutil.c
tests/ChangeLog
tests/Makefile.am
tests/t-mpi-bit.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index e4bbad4..ca293d8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,9 +12,11 @@ Noteworthy changes in version 1.3.0 (unreleased)
 
  * Support for SHA-224 and HMAC using SHA-384 and SHA-512.
 
+ * gcry_mpi_rshift does not anymore truncate the shift count.
+
  * Interface changes relative to the 1.2.0 release:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- gcry_fast_random_poll NEW
+ gcry_fast_random_poll  NEW
  gcry_pk_algo_name       CHANGED (minor change in respect to return value)
  gcry_cipher_algo_name   CHANGED (minor change in respect to return value)
  GCRY_MD_SHA224          NEW
diff --git a/TODO b/TODO
index 365d8f7..1cfc6dc 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,5 +1,7 @@
 What's left to do                                 -*- outline -*-
 
+* Updated the FSF's directory.
+
 * Add more tests.
 
 * udiv-qrnbd.o should get build as *.lo [HPUX]
index a6cb45b..c921a1a 100644 (file)
@@ -1,3 +1,15 @@
+2006-08-03  Werner Koch  <wk@g10code.com>
+
+       * random-daemon.c (_gcry_daemon_initialize_basics): Don't
+       initialize the socket.  Remove arg SOCKETNAME.
+       (connect_to_socket): Make sure that daemon is set to -1 on error.
+       (call_daemon): Initialize the socket on the first call.
+       (_gcry_daemon_randomize, _gcry_daemon_get_random_bytes) 
+       (_gcry_daemon_create_nonce): New arg SOCKETNAME.
+       * random.c (initialize): Call new daemon initializator.
+       (get_random_bytes, gcry_randomize, gcry_create_nonce): Pass socket
+       name to daemon call and reset allow_daemon on failure.
+
 2006-07-26  Werner Koch  <wk@g10code.com>
 
        * rmd160.c (_gcry_rmd160_mixblock): Add cast to transform call.
index 6a42d55..95d76f7 100644 (file)
@@ -24,7 +24,7 @@
    gcryptrnd.  Such a daemon is useful to keep a persistent pool in
    memory over invocations of a single application and to allow
    prioritizing access to the actual entropy sources.  The drawback is
-   that we need to use IPC (i.e. unxi domain socket) to convey
+   that we need to use IPC (i.e. unix domain socket) to convey
    sensitive data.
  */
 
@@ -111,20 +111,22 @@ connect_to_socket (const char *socketname, int *sock)
 
  out:
 
-  *sock = fd;
   gcry_free (srvr_addr);
-
   if (err)
-    close (fd);
+    {
+      close (fd);
+      fd = -1;
+    }
+  *sock = fd;
 
   return err;
 }
 
 
 /* Initialize basics of this module. This should be viewed as a
-   constroctur to prepare locking. */
+   constructor to prepare locking. */
 void
-_gcry_daemon_initialize_basics (const char *socketname)
+_gcry_daemon_initialize_basics (void)
 {
   static int initialized;
   int err;
@@ -135,15 +137,10 @@ _gcry_daemon_initialize_basics (const char *socketname)
       err = ath_mutex_init (&daemon_lock);
       if (err)
         log_fatal ("failed to create the daemon lock: %s\n", strerror (err) );
-
-      err = connect_to_socket (socketname ? socketname : RANDOM_DAEMON_SOCKET,
-                              &daemon_socket);
-      if (err)
-       log_info ("not using random daemon\n");
     }
 }
 
-\f
+
 
 /* Send LENGTH bytes of BUFFER to file descriptor FD.  Returns 0 on
    success or another value on write error. */
@@ -201,22 +198,47 @@ readn (int fd, void *buf, size_t buflen, size_t *ret_nread)
    data of random level LEVEL will be generated.  The retrieved random
    data will be stored in BUFFER.  Returns error code.  */
 static gcry_error_t
-call_daemon (void *buffer, size_t req_nbytes, int nonce,
+call_daemon (const char *socketname,
+             void *buffer, size_t req_nbytes, int nonce,
             enum gcry_random_level level)
 {
+  static int initialized;
   unsigned char buf[255];
-  gcry_error_t err;
+  gcry_error_t err = 0;
   size_t nbytes;
   size_t nread;
   int rc;
 
-  err = 0;
-
-  if (! req_nbytes)
+  if (!req_nbytes)
     return 0;
 
   ath_mutex_lock (&daemon_lock);
 
+  /* Open the socket if that has not been done. */
+  if (!initialized)
+    {
+      initialized = 1;
+      err = connect_to_socket (socketname ? socketname : RANDOM_DAEMON_SOCKET,
+                              &daemon_socket);
+      if (err)
+        {
+          daemon_socket = -1;
+          log_info ("not using random daemon\n");
+          ath_mutex_unlock (&daemon_lock);
+          return err;
+        }
+    }
+
+  /* Check that we have a valid socket descriptor. */
+  if ( daemon_socket == -1 )
+    {
+      ath_mutex_unlock (&daemon_lock);
+      return gcry_error (GPG_ERR_INTERNAL);
+    }
+
+
+  /* Do the real work.  */
+
   do
     {
       /* Process in chunks.  */
@@ -311,12 +333,13 @@ call_daemon (void *buffer, size_t req_nbytes, int nonce,
    support GCRY_STRONG_RANDOM and GCRY_VERY_STRONG_RANDOM here.
    Return 0 on success. */
 int
-_gcry_daemon_randomize (void *buffer, size_t length,
+_gcry_daemon_randomize (const char *socketname, 
+                        void *buffer, size_t length,
                         enum gcry_random_level level)
 {
   gcry_error_t err;
 
-  err = call_daemon (buffer, length, 0, level);
+  err = call_daemon (socketname, buffer, length, 0, level);
 
   return err ? -1 : 0;
 }
@@ -327,7 +350,8 @@ _gcry_daemon_randomize (void *buffer, size_t length,
    that the IPC mechanism might have not stored it there.  Return a
    pointer to a newly alloced memory or NULL if it failed.  */
 void *
-_gcry_daemon_get_random_bytes (size_t nbytes, int level, int secure)
+_gcry_daemon_get_random_bytes (const char *socketname,
+                               size_t nbytes, int level, int secure)
 {
   gcry_error_t err;
   void *p;
@@ -336,7 +360,7 @@ _gcry_daemon_get_random_bytes (size_t nbytes, int level, int secure)
   if (err)
     goto out;
 
-  err = call_daemon (p, nbytes, 0, level);
+  err = call_daemon (socketname, p, nbytes, 0, level);
 
  out:
 
@@ -353,11 +377,11 @@ _gcry_daemon_get_random_bytes (size_t nbytes, int level, int secure)
 /* Internal function to fill BUFFER with NBYTES of data usable for a
    nonce.  Returns 0 on success. */
 int
-_gcry_daemon_create_nonce (void *buffer, size_t length)
+_gcry_daemon_create_nonce (const char *socketname, void *buffer, size_t length)
 {
   gcry_error_t err;
 
-  err = call_daemon (buffer, length, 1, 0);
+  err = call_daemon (socketname, buffer, length, 1, 0);
 
   return err ? -1 : 0;
 }
index e38b877..528111a 100644 (file)
@@ -161,7 +161,7 @@ initialize_basics(void)
       if (err)
         log_fatal ("failed to create the nonce buffer lock: %s\n",
                    strerror (err) );
-      _gcry_daemon_initialize_basics (daemon_socket_name);
+      _gcry_daemon_initialize_basics ();
     }
 }
 
@@ -304,8 +304,11 @@ get_random_bytes ( size_t nbytes, int level, int secure)
   /* Make sure the requested level is in range. */
   MASK_LEVEL(level);
 
-  if (allow_daemon && (p=_gcry_daemon_get_random_bytes (nbytes, level,secure)))
+  if (allow_daemon &&
+      (p=_gcry_daemon_get_random_bytes (daemon_socket_name,
+                                        nbytes, level,secure)))
     return p; /* The daemon succeeded. */
+  allow_daemon = 0; /* Daemon failed - switch off. */
 
   /* Lock the pool. */
   err = ath_mutex_lock (&pool_lock);
@@ -417,8 +420,10 @@ gcry_randomize (byte *buffer, size_t length, enum gcry_random_level level)
   /* Make sure the level is okay. */
   MASK_LEVEL(level);
 
-  if (allow_daemon && !_gcry_daemon_randomize (buffer, length, level))
+  if (allow_daemon
+      && !_gcry_daemon_randomize (daemon_socket_name, buffer, length, level))
     return; /* The daemon succeeded. */
+  allow_daemon = 0; /* Daemon failed - switch off. */
 
   /* Acquire the pool lock. */
   err = ath_mutex_lock (&pool_lock);
@@ -1233,8 +1238,10 @@ gcry_create_nonce (unsigned char *buffer, size_t length)
   if (!is_initialized)
     initialize ();
 
-  if (allow_daemon && !_gcry_daemon_create_nonce (buffer, length))
+  if (allow_daemon
+      && !_gcry_daemon_create_nonce (daemon_socket_name, buffer, length))
     return; /* The daemon succeeded. */
+  allow_daemon = 0; /* Daemon failed - switch off. */
 
   /* Acquire the nonce buffer lock. */
   err = ath_mutex_lock (&nonce_buffer_lock);
index afed64c..3c9c75f 100644 (file)
@@ -37,12 +37,14 @@ byte *_gcry_get_random_bits( size_t nbits, int level, int secure );
 void _gcry_fast_random_poll( void );
 
 /*-- random-daemon.c (only used from random.c) --*/
-void _gcry_set_random_daemon_socket (const char *socketname);
-void _gcry_daemon_initialize_basics (const char *socketname);
-int _gcry_daemon_randomize (void *buffer, size_t length,
+void _gcry_daemon_initialize_basics (void);
+int _gcry_daemon_randomize (const char *socketname,
+                            void *buffer, size_t length,
                             enum gcry_random_level level);
-void *_gcry_daemon_get_random_bytes (size_t nbytes, int level, int secure);
-int _gcry_daemon_create_nonce (void *buffer, size_t length);
+void *_gcry_daemon_get_random_bytes (const char *socketname,
+                                     size_t nbytes, int level, int secure);
+int _gcry_daemon_create_nonce (const char *socketname,
+                               void *buffer, size_t length);
 
 
 #endif /*G10_RANDOM_H*/
index c82cc6c..176ccf2 100644 (file)
@@ -1,3 +1,15 @@
+2006-08-04  Werner Koch  <wk@g10code.com>
+
+       * mpi-bit.c (gcry_mpi_rshift): Rewritten to remove the limitation
+       on N (which used to be less than BITS_PER_MPI_LIMB).
+
+2006-08-03  Werner Koch  <wk@g10code.com>
+
+       * mpi-bit.c (gcry_mpi_set_bit, gcry_mpi_set_highbit): Fixed
+       allocation.  Reported by bpgcrypt at itaparica.org.
+       * mpiutil.c (_gcry_mpi_resize): Clear the new part of the resized
+       limb space.
+
 2006-07-26  Werner Koch  <wk@g10code.com>
 
        * mpiutil.c (gcry_mpi_randomize): Changed P to unsigned char*.
index 2d91799..d16eaae 100644 (file)
@@ -1,5 +1,5 @@
 /* mpi-bit.c  -  MPI bit level fucntions
- * Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2001, 2002, 2006 Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
@@ -116,17 +116,17 @@ gcry_mpi_test_bit( gcry_mpi_t a, unsigned int n )
 void
 gcry_mpi_set_bit( gcry_mpi_t a, unsigned int n )
 {
-    unsigned int limbno, bitno;
+  unsigned int limbno, bitno;
 
-    limbno = n / BITS_PER_MPI_LIMB;
-    bitno  = n % BITS_PER_MPI_LIMB;
+  limbno = n / BITS_PER_MPI_LIMB;
+  bitno  = n % BITS_PER_MPI_LIMB;
 
-    if( limbno >= a->nlimbs ) { /* resize */
-       if( a->alloced >= limbno )
-           mpi_resize(a, limbno+1 );
-       a->nlimbs = limbno+1;
+  if ( limbno >= a->nlimbs ) 
+    {
+      mpi_resize (a, limbno+1 );
+      a->nlimbs = limbno+1;
     }
-    a->d[limbno] |= (A_LIMB_1<<bitno);
+  a->d[limbno] |= (A_LIMB_1<<bitno);
 }
 
 /****************
@@ -135,20 +135,20 @@ gcry_mpi_set_bit( gcry_mpi_t a, unsigned int n )
 void
 gcry_mpi_set_highbit( gcry_mpi_t a, unsigned int n )
 {
-    unsigned int limbno, bitno;
-
-    limbno = n / BITS_PER_MPI_LIMB;
-    bitno  = n % BITS_PER_MPI_LIMB;
-
-    if( limbno >= a->nlimbs ) { /* resize */
-       if( a->alloced >= limbno )
-           mpi_resize(a, limbno+1 );
-       a->nlimbs = limbno+1;
+  unsigned int limbno, bitno;
+  
+  limbno = n / BITS_PER_MPI_LIMB;
+  bitno  = n % BITS_PER_MPI_LIMB;
+  
+  if ( limbno >= a->nlimbs ) 
+    { 
+      mpi_resize (a, limbno+1 );
+      a->nlimbs = limbno+1;
     }
-    a->d[limbno] |= (A_LIMB_1<<bitno);
-    for( bitno++; bitno < BITS_PER_MPI_LIMB; bitno++ )
-       a->d[limbno] &= ~(A_LIMB_1 << bitno);
-    a->nlimbs = limbno+1;
+  a->d[limbno] |= (A_LIMB_1<<bitno);
+  for ( bitno++; bitno < BITS_PER_MPI_LIMB; bitno++ )
+    a->d[limbno] &= ~(A_LIMB_1 << bitno);
+  a->nlimbs = limbno+1;
 }
 
 /****************
@@ -188,26 +188,77 @@ gcry_mpi_clear_bit( gcry_mpi_t a, unsigned int n )
 }
 
 
-/****************
- * Shift A by N bits to the right
- * FIXME: should use alloc_limb if X and A are same.
+/*
+ * Shift A by N bits to the right.
  */
 void
-gcry_mpi_rshift( gcry_mpi_t x, gcry_mpi_t a, unsigned n )
+gcry_mpi_rshift ( gcry_mpi_t x, gcry_mpi_t a, unsigned int n )
 {
-    mpi_ptr_t xp;
-    mpi_size_t xsize;
-
-    xsize = a->nlimbs;
-    x->sign = a->sign;
-    RESIZE_IF_NEEDED(x, xsize);
-    xp = x->d;
-
-    if( xsize ) {
-       _gcry_mpih_rshift( xp, a->d, xsize, n);
-       MPN_NORMALIZE( xp, xsize);
+  mpi_size_t xsize;
+  unsigned int i;
+  unsigned int nlimbs = (n/BITS_PER_MPI_LIMB);
+  unsigned int nbits = (n%BITS_PER_MPI_LIMB);
+
+  if ( x == a )
+    {
+      /* In-place operation.  */
+      if ( nlimbs >= x->nlimbs )
+        {
+          x->nlimbs = 0;
+          return;
+        }
+
+      if (nlimbs)
+        {
+          for (i=0; i < x->nlimbs - nlimbs; i++ )
+            x->d[i] = x->d[i+nlimbs];
+          x->d[i] = 0;
+          x->nlimbs -= nlimbs;
+
+        }
+      if ( x->nlimbs && nbits )
+        _gcry_mpih_rshift ( x->d, x->d, x->nlimbs, nbits );
+    }
+  else if ( nlimbs )
+    {
+      /* Copy and shift by more or equal bits than in a limb. */
+      xsize = a->nlimbs;
+      x->sign = a->sign;
+      RESIZE_IF_NEEDED (x, xsize);
+      x->nlimbs = xsize;
+      for (i=0; i < a->nlimbs; i++ )
+        x->d[i] = a->d[i];
+      x->nlimbs = i;
+
+      if ( nlimbs >= x->nlimbs )
+        {
+          x->nlimbs = 0;
+          return;
+        }
+
+      if (nlimbs)
+        {
+          for (i=0; i < x->nlimbs - nlimbs; i++ )
+            x->d[i] = x->d[i+nlimbs];
+          x->d[i] = 0;
+          x->nlimbs -= nlimbs;
+        }
+
+      if ( x->nlimbs && nbits )
+        _gcry_mpih_rshift ( x->d, x->d, x->nlimbs, nbits );
+    }
+  else
+    {
+      /* Copy and shift by less than bits in a limb.  */
+      xsize = a->nlimbs;
+      x->sign = a->sign;
+      RESIZE_IF_NEEDED (x, xsize);
+      x->nlimbs = xsize;
+      
+      if ( xsize )
+        _gcry_mpih_rshift (x->d, a->d, x->nlimbs, nbits );
     }
-    x->nlimbs = xsize;
+  MPN_NORMALIZE (x->d, x->nlimbs);
 }
 
 
index f6f9a8b..fe1b761 100644 (file)
@@ -115,17 +115,30 @@ _gcry_mpi_assign_limb_space( gcry_mpi_t a, mpi_ptr_t ap, unsigned int nlimbs )
 
 
 /****************
- * Resize the array of A to NLIMBS. the additional space is cleared
- * (set to 0) [done by gcry_realloc()]
+ * Resize the array of A to NLIMBS. The additional space is cleared
+ * (set to 0).
  */
 void
 _gcry_mpi_resize (gcry_mpi_t a, unsigned nlimbs)
 {
+  size_t i;
+
   if (nlimbs <= a->alloced)
-    return; /* no need to do it */
+    {
+      /* We only need to clear the new space (this is a nop if the
+         limb space is already of the correct size. */
+      for (i=a->nlimbs; i < a->alloced; i++)
+        a->d[i] = 0;
+      return; 
+    }
 
+  /* Actually resize the limb space.  */
   if (a->d)
-    a->d = gcry_xrealloc (a->d, nlimbs * sizeof (mpi_limb_t));
+    {
+      a->d = gcry_xrealloc (a->d, nlimbs * sizeof (mpi_limb_t));
+      for (i=a->alloced; i < nlimbs; i++)
+        a->d[i] = 0;
+    }
   else
     {
       if (a->flags & 1)
index 154bd5e..af66e6e 100644 (file)
@@ -1,3 +1,7 @@
+2006-08-03  Werner Koch  <wk@g10code.com>
+
+       * t-mpi-bit.c: New.
+
 2006-07-06  Werner Koch  <wk@g10code.com>
 
        * benchmark.c (main): New option --use-random-daemon.  New command
index 945ce5d..e4a922a 100644 (file)
@@ -18,7 +18,7 @@
 
 ## Process this file with automake to produce Makefile.in
 
-TESTS = prime register ac ac-schemes ac-data basic \
+TESTS = t-mpi-bit prime register ac ac-schemes ac-data basic \
         tsexp keygen pubkey benchmark pkbench hmac keygrip
 
 INCLUDES = -I$(top_srcdir)/src
diff --git a/tests/t-mpi-bit.c b/tests/t-mpi-bit.c
new file mode 100644 (file)
index 0000000..2465a8f
--- /dev/null
@@ -0,0 +1,241 @@
+/* t-mpi-bit.c  - Tests for bit level functions
+ * Copyright (C) 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
+ * MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "../src/gcrypt.h"
+
+#define PGM "t-mpi-bit"
+
+static const char *wherestr;
+static int verbose;
+static int error_count;
+
+#define xmalloc(a)    gcry_xmalloc ((a))
+#define xcalloc(a,b)  gcry_xcalloc ((a),(b))
+#define xfree(a)      gcry_free ((a))
+#define pass() do { ; } while (0)
+
+static void
+show (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  if (!verbose)
+    return;
+  fprintf (stderr, "%s: ", PGM);
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+}
+
+static void
+fail (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  fflush (stdout);
+  fprintf (stderr, "%s: ", PGM);
+  if (wherestr)
+    fprintf (stderr, "%s: ", wherestr);
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  error_count++;
+}
+
+static void
+die (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  fflush (stdout);
+  fprintf (stderr, "%s: ", PGM);
+  if (wherestr)
+    fprintf (stderr, "%s: ", wherestr);
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  exit (1);
+}
+
+/* Allocate a bit string consisting of '0' and '1' from the MPI
+   A. Return the LENGTH least significant bits. Caller needs to xfree
+   the result. */
+static char *
+mpi2bitstr (gcry_mpi_t a, size_t length)
+{
+  char *p, *buf;
+  
+  buf = p = xmalloc (length+1);
+  while (length--)
+    *p++ = gcry_mpi_test_bit (a, length) ? '1':'0';
+  *p = 0;
+
+  return buf;
+}
+
+/* Shift a bit string to the right. */
+static void
+rshiftbitstring (char *string, size_t n)
+{
+  size_t len = strlen (string);
+
+  if (n > len)
+    n = len;
+
+  memmove (string+n, string, len-n);
+  memset (string, '0', n);
+}
+
+
+/* This is to check a bug reported by bpgcrypt at itaparica.org on
+   2006-07-31 against libgcrypt 1.2.2.  */
+static void
+one_bit_only (int highbit)
+{
+  gcry_mpi_t a;
+  char *result;
+  int i;
+
+  wherestr = "one_bit_only";
+  show ("checking that set_%sbit does only set one bit\n", highbit?"high":"");
+
+  a = gcry_mpi_new (0);
+  gcry_mpi_randomize (a, 70, GCRY_WEAK_RANDOM);
+  gcry_mpi_set_ui (a, 0);
+
+  if (highbit)
+    gcry_mpi_set_highbit (a, 42);
+  else
+    gcry_mpi_set_bit (a, 42);
+  if (!gcry_mpi_test_bit (a, 42))
+    fail ("failed to set a bit\n");
+  gcry_mpi_clear_bit (a, 42);
+  if (gcry_mpi_test_bit (a, 42))
+    fail ("failed to clear a bit\n");
+  result = mpi2bitstr (a, 70);
+  assert (strlen (result) == 70);
+  show ("r=%s\n", result);
+  for (i=0; result[i]; i++)
+    if ( result[i] != '0' )
+      break;
+  if (result[i])
+    fail ("spurious bits detected\n");
+  xfree (result);
+  gcry_mpi_release (a);
+}
+
+/* Check that the shifting actually works for an amount larger than
+   the number of bits per limb. */
+static void
+test_rshift (int pass)
+{
+  gcry_mpi_t a, b;
+  char *result, *result2;
+  int i;
+
+  wherestr = "test_rshift";
+  show ("checking that rshift works as expected (pass %d)\n", pass);
+
+  a = gcry_mpi_new (0);
+  b = gcry_mpi_new (0);
+  gcry_mpi_randomize (a, 70, GCRY_WEAK_RANDOM);
+
+  for (i=0; i < 75; i++)
+    {
+      gcry_mpi_rshift (b, a, i);
+
+      result = mpi2bitstr (b, 72);
+      result2 = mpi2bitstr (a, 72);
+      rshiftbitstring (result2, i);
+      if (strcmp (result, result2))
+        {
+          show ("got =%s\n", result);
+          show ("want=%s\n", result2);
+          fail ("rshift by %d failed\n", i);
+        }
+      xfree (result);
+      xfree (result2);
+    }
+
+  /* Again. This time using in-place operation. */
+  gcry_mpi_randomize (a, 70, GCRY_WEAK_RANDOM);
+
+  for (i=0; i < 75; i++)
+    {
+      gcry_mpi_release (b);
+      b = gcry_mpi_copy (a);
+      gcry_mpi_rshift (b, b, i);
+
+      result = mpi2bitstr (b, 72);
+      result2 = mpi2bitstr (a, 72);
+      rshiftbitstring (result2, i);
+      if (strcmp (result, result2))
+        {
+          show ("got =%s\n", result);
+          show ("want=%s\n", result2);
+          fail ("in-place rshift by %d failed\n", i);
+        }
+      xfree (result2);
+      xfree (result);
+    }
+
+  gcry_mpi_release (b);
+  gcry_mpi_release (a);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int debug = 0;
+  int i;
+
+  if (argc > 1 && !strcmp (argv[1], "--verbose"))
+    verbose = 1;
+  else if (argc > 1 && !strcmp (argv[1], "--debug"))
+    verbose = debug = 1;
+
+  if (!gcry_check_version (GCRYPT_VERSION))
+    die ("version mismatch\n");
+
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+  if (debug)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+
+  one_bit_only (0);
+  one_bit_only (1);
+  for (i=0; i < 5; i++)
+    test_rshift (i); /* Run several times due to random initializations. */
+  
+  show ("All tests completed. Errors: %d\n", error_count);
+  return error_count ? 1 : 0;
+}