Fixed leading zero problems in PSS and OAEP. ueno-pss
authorWerner Koch <wk@gnupg.org>
Fri, 10 Jun 2011 08:52:18 +0000 (10:52 +0200)
committerWerner Koch <wk@gnupg.org>
Fri, 10 Jun 2011 08:52:18 +0000 (10:52 +0200)
NEWS
cipher/ChangeLog
cipher/pubkey.c
doc/gcrypt.texi
src/ChangeLog
src/sexp.c
tests/ChangeLog
tests/basic.c
tests/pkcs1v2.c

diff --git a/NEWS b/NEWS
index 0522b1f..d3e3b95 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,7 +10,9 @@ Noteworthy changes in version 1.5.x (unreleased)
 
  * Support for OAEP and PSS methods as described by RFC-3447.
 
- * gcry_sexp_build does now support opaque MPIs with "%m".
+ * New format specifiers "%M" and "%u" for gcry_sexp_build.
+
+ * gcry_sexp_build does now support opaque MPIs with "%m" and "%M".
 
  * New functions gcry_pk_get_curve and gcry_pk_get_param to map ECC
    parameters to a curve name and to retrieve parameter values.
@@ -54,8 +56,10 @@ Noteworthy changes in version 1.5.x (unreleased)
  gcry_pk_get_param          NEW.
  GCRYCTL_DISABLE_HWF        NEW.
  gcry_kdf_derive            NEW.
- gcry_pk_encrypt            EXTENDED:  Support OAEP
- gcry_pk_decrypt            EXTENDED:  Support OAEP
+ gcry_pk_encrypt            EXTENDED: Support OAEP
+ gcry_pk_decrypt            EXTENDED: Support OAEP
+ gcry_sexp_build            EXTENDED: Add format specifiers M and u.
+
 
  * Interface changes relative to the 1.4.2 release:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 7bc13b3..86762be 100644 (file)
@@ -1,3 +1,10 @@
+2011-06-10  Werner Koch  <wk@g10code.com>
+
+       * pubkey.c (gcry_pk_sign): Use format specifier '%M' to avoid
+       leading zeroes.  Special case output generation for PSS.
+       (gcry_pk_encrypt): Special case output generation for OAEP.
+       (sexp_data_to_mpi): Use GCRYMPI_FMT_USG for PSS verify.
+
 2011-06-09  Werner Koch  <wk@g10code.com>
 
        * pubkey.c (oaep_decode): Make use of octet_string_from_mpi.
index 9b6d24e..51dc40f 100644 (file)
@@ -2712,7 +2712,7 @@ sexp_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
             rc = GPG_ERR_DIGEST_ALGO;
          else
            {
-             *ret_mpi = gcry_sexp_nth_mpi (lhash, 2, 0);
+             *ret_mpi = gcry_sexp_nth_mpi (lhash, 2, GCRYMPI_FMT_USG);
              if (!*ret_mpi)
                rc = GPG_ERR_INV_OBJ;
              ctx->verify_cmp = pss_verify_cmp;
@@ -2831,50 +2831,70 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey)
     goto leave;
 
   /* We did it.  Now build the return list */
-  {
-    char *string, *p;
-    int i;
-    size_t nelem = strlen (algo_elems);
-    size_t needed = 19 + strlen (algo_name) + (nelem * 5);
-    void **arg_list;
+  if (ctx.encoding == PUBKEY_ENC_OAEP)
+    {
+      /* We need to make sure to return the correct length to avoid
+         problems with missing leading zeroes.  We know that this
+         encoding does only make sense with RSA thus we don't need to
+         build the S-expression on the fly.  */
+      unsigned char *em;
+      size_t emlen = (ctx.nbits+7)/8;
 
-    /* Build the string.  */
-    string = p = gcry_malloc (needed);
-    if (!string)
-      {
-        rc = gpg_err_code_from_syserror ();
+      rc = octet_string_from_mpi (&em, NULL, ciph[0], emlen);
+      if (rc)
         goto leave;
-      }
-    p = stpcpy ( p, "(enc-val(" );
-    p = stpcpy ( p, algo_name );
-    for (i=0; algo_elems[i]; i++ )
-      {
-        *p++ = '(';
-        *p++ = algo_elems[i];
-        p = stpcpy ( p, "%m)" );
-      }
-    strcpy ( p, "))" );
-
-    /* And now the ugly part: We don't have a function to pass an
-     * array to a format string, so we have to do it this way :-(.  */
-    /* FIXME: There is now such a format specifier, so we can
-       change the code to be more clear. */
-    arg_list = malloc (nelem * sizeof *arg_list);
-    if (!arg_list)
-      {
-        rc = gpg_err_code_from_syserror ();
+      rc = gcry_err_code (gcry_sexp_build (r_ciph, NULL,
+                                           "(enc-val(%s(a%b)))",
+                                           algo_name, (int)emlen, em));
+      gcry_free (em);
+      if (rc)
         goto leave;
-      }
+    }
+  else
+    {
+      char *string, *p;
+      int i;
+      size_t nelem = strlen (algo_elems);
+      size_t needed = 19 + strlen (algo_name) + (nelem * 5);
+      void **arg_list;
 
-    for (i = 0; i < nelem; i++)
-      arg_list[i] = ciph + i;
+      /* Build the string.  */
+      string = p = gcry_malloc (needed);
+      if (!string)
+        {
+          rc = gpg_err_code_from_syserror ();
+          goto leave;
+        }
+      p = stpcpy ( p, "(enc-val(" );
+      p = stpcpy ( p, algo_name );
+      for (i=0; algo_elems[i]; i++ )
+        {
+          *p++ = '(';
+          *p++ = algo_elems[i];
+          p = stpcpy ( p, "%m)" );
+        }
+      strcpy ( p, "))" );
 
-    rc = gcry_sexp_build_array (r_ciph, NULL, string, arg_list);
-    free (arg_list);
-    if (rc)
-      BUG ();
-    gcry_free (string);
-  }
+      /* And now the ugly part: We don't have a function to pass an
+       * array to a format string, so we have to do it this way :-(.  */
+      /* FIXME: There is now such a format specifier, so we can
+         change the code to be more clear. */
+      arg_list = malloc (nelem * sizeof *arg_list);
+      if (!arg_list)
+        {
+          rc = gpg_err_code_from_syserror ();
+          goto leave;
+        }
+
+      for (i = 0; i < nelem; i++)
+        arg_list[i] = ciph + i;
+
+      rc = gcry_sexp_build_array (r_ciph, NULL, string, arg_list);
+      free (arg_list);
+      if (rc)
+        BUG ();
+      gcry_free (string);
+    }
 
  leave:
   if (pkey)
@@ -3102,49 +3122,71 @@ gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey)
   if (rc)
     goto leave;
 
-  {
-    char *string, *p;
-    size_t nelem, needed = strlen (algo_name) + 20;
-    void **arg_list;
+  /* FIXME:  Shall we do such a special case also for pkcs#1 encoding?  */
+  if (ctx.encoding == PUBKEY_ENC_PSS)
+    {
+      /* We need to make sure to return the correct length to avoid
+         problems with missing leading zeroes.  We know that this
+         encoding does only make sense with RSA thus we don't need to
+         build the S-expression on the fly.  */
+      unsigned char *em;
+      size_t emlen = (ctx.nbits+7)/8;
 
-    nelem = strlen (algo_elems);
+      rc = octet_string_from_mpi (&em, NULL, result[0], emlen);
+      if (rc)
+        goto leave;
+      rc = gcry_err_code (gcry_sexp_build (r_sig, NULL,
+                                           "(sig-val(%s(s%b)))",
+                                           algo_name, (int)emlen, em));
+      gcry_free (em);
+      if (rc)
+        goto leave;
+    }
+  else
+    {
+      /* General purpose output encoding.  Do it on the fly.  */
+      char *string, *p;
+      size_t nelem, needed = strlen (algo_name) + 20;
+      void **arg_list;
 
-    /* Count elements, so that we can allocate enough space. */
-    needed += 10 * nelem;
+      nelem = strlen (algo_elems);
 
-    /* Build the string. */
-    string = p = gcry_malloc (needed);
-    if (!string)
-      {
-        rc = gpg_err_code_from_syserror ();
-        goto leave;
-      }
-    p = stpcpy (p, "(sig-val(");
-    p = stpcpy (p, algo_name);
-    for (i = 0; algo_elems[i]; i++)
-      {
-        *p++ = '(';
-        *p++ = algo_elems[i];
-        p = stpcpy (p, "%m)");
-      }
-    strcpy (p, "))");
+      /* Count elements, so that we can allocate enough space. */
+      needed += 10 * nelem;
 
-    arg_list = malloc (nelem * sizeof *arg_list);
-    if (!arg_list)
-      {
-        rc = gpg_err_code_from_syserror ();
-        goto leave;
-      }
+      /* Build the string. */
+      string = p = gcry_malloc (needed);
+      if (!string)
+        {
+          rc = gpg_err_code_from_syserror ();
+          goto leave;
+        }
+      p = stpcpy (p, "(sig-val(");
+      p = stpcpy (p, algo_name);
+      for (i = 0; algo_elems[i]; i++)
+        {
+          *p++ = '(';
+          *p++ = algo_elems[i];
+          p = stpcpy (p, "%M)");
+        }
+      strcpy (p, "))");
 
-    for (i = 0; i < nelem; i++)
-      arg_list[i] = result + i;
+      arg_list = malloc (nelem * sizeof *arg_list);
+      if (!arg_list)
+        {
+          rc = gpg_err_code_from_syserror ();
+          goto leave;
+        }
 
-    rc = gcry_sexp_build_array (r_sig, NULL, string, arg_list);
-    free (arg_list);
-    if (rc)
-      BUG ();
-    gcry_free (string);
-  }
+      for (i = 0; i < nelem; i++)
+        arg_list[i] = result + i;
+
+      rc = gcry_sexp_build_array (r_sig, NULL, string, arg_list);
+      free (arg_list);
+      if (rc)
+        BUG ();
+      gcry_free (string);
+    }
 
  leave:
   if (skey)
index b3dc127..bdfe955 100644 (file)
@@ -4155,13 +4155,21 @@ expects arguments for some of these escape sequences right after
 @table @samp
 @item %m
 The next argument is expected to be of type @code{gcry_mpi_t} and a copy of
-its value is inserted into the resulting S-expression.
+its value is inserted into the resulting S-expression.  The MPI is
+stored as a signed integer.
+@item %M
+The next argument is expected to be of type @code{gcry_mpi_t} and a copy of
+its value is inserted into the resulting S-expression.  The MPI is
+stored as an unsigned integer.
 @item %s
 The next argument is expected to be of type @code{char *} and that
 string is inserted into the resulting S-expression.
 @item %d
 The next argument is expected to be of type @code{int} and its value is
 inserted into the resulting S-expression.
+@item %u
+The next argument is expected to be of type @code{unsigned int} and
+its value is inserted into the resulting S-expression.
 @item %b
 The next argument is expected to be of type @code{int} directly
 followed by an argument of type @code{char *}.  This represents a
index aaaf9a4..8506532 100644 (file)
@@ -1,3 +1,7 @@
+2011-06-10  Werner Koch  <wk@g10code.com>
+
+       * sexp.c (vsexp_sscan): Add new format specifiers 'M' and 'u'.
+
 2011-05-24  Daiki Ueno  <ueno@unixuser.org>
 
        * cipher.h (pk_operation): New.
index ed4e940..0877773 100644 (file)
@@ -1,6 +1,6 @@
 /* sexp.c  -  S-Expression handling
  * Copyright (C) 1999, 2000, 2001, 2002, 2003,
- *               2004, 2006, 2007, 2008  Free Software Foundation, Inc.
+ *               2004, 2006, 2007, 2008, 2011  Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
@@ -1249,11 +1249,12 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
        }
       else if (percent)
        {
-         if (*p == 'm')
+         if (*p == 'm' || *p == 'M')
            {
              /* Insert an MPI.  */
              gcry_mpi_t m;
              size_t nm = 0;
+              int mpifmt = *p == 'm'? GCRYMPI_FMT_STD: GCRYMPI_FMT_USG;
 
              ARG_NEXT (m, gcry_mpi_t);
 
@@ -1296,7 +1297,7 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
                 }
               else
                 {
-                  if (gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &nm, m))
+                  if (gcry_mpi_print (mpifmt, NULL, 0, &nm, m))
                     BUG ();
 
                   MAKE_SPACE (nm);
@@ -1323,7 +1324,7 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
 
                   *c.pos++ = ST_DATA;
                   STORE_LEN (c.pos, nm);
-                  if (gcry_mpi_print (GCRYMPI_FMT_STD, c.pos, nm, &nm, m))
+                  if (gcry_mpi_print (mpifmt, c.pos, nm, &nm, m))
                     BUG ();
                   c.pos += nm;
                 }
@@ -1385,7 +1386,7 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
              /* Insert an integer as string.  */
              int aint;
              size_t alen;
-             char buf[20];
+             char buf[35];
 
              ARG_NEXT (aint, int);
              sprintf (buf, "%d", aint);
@@ -1396,6 +1397,22 @@ vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
              memcpy (c.pos, buf, alen);
              c.pos += alen;
            }
+         else if (*p == 'u')
+           {
+             /* Insert an unsigned integer as string.  */
+             unsigned int aint;
+             size_t alen;
+             char buf[35];
+
+             ARG_NEXT (aint, unsigned int);
+             sprintf (buf, "%u", aint);
+             alen = strlen (buf);
+             MAKE_SPACE (alen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, alen);
+             memcpy (c.pos, buf, alen);
+             c.pos += alen;
+           }
          else if (*p == 'S')
            {
              /* Insert a gcry_sexp_t.  */
index d35c955..289d2ec 100644 (file)
@@ -1,3 +1,7 @@
+2011-06-10  Werner Koch  <wk@g10code.com>
+
+       * basic.c (check_pubkey_crypt): Run OAEP tests only for RSA.
+
 2011-06-09  Werner Koch  <wk@g10code.com>
 
        * pkcs1v2.c: New.
index 95cc749..ca275f7 100644 (file)
@@ -2391,6 +2391,7 @@ check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
   int dataidx;
   static struct
   {
+    int algo;    /* If not 0 run test only if ALGO matches.  */
     const char *data;
     const char *hint;
     int unpadded;
@@ -2398,94 +2399,111 @@ check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
     int decrypt_expected_rc;
   } datas[] =
     {
-      {        "(data\n (flags pkcs1)\n"
+      {        0,
+        "(data\n (flags pkcs1)\n"
        " (value #11223344556677889900AA#))\n",
        NULL,
        0,
        0,
        0 },
-      {        "(data\n (flags pkcs1)\n"
+      {        0,
+        "(data\n (flags pkcs1)\n"
        " (value #11223344556677889900AA#))\n",
        "(flags pkcs1)",
        1,
        0,
        0 },
-      { "(data\n (flags oaep)\n"
+      { GCRY_PK_RSA,
+        "(data\n (flags oaep)\n"
        " (value #11223344556677889900AA#))\n",
        "(flags oaep)",
        1,
        0,
        0 },
-      { "(data\n (flags oaep)\n (hash-algo sha1)\n"
+      { GCRY_PK_RSA,
+        "(data\n (flags oaep)\n (hash-algo sha1)\n"
        " (value #11223344556677889900AA#))\n",
        "(flags oaep)(hash-algo sha1)",
        1,
        0,
        0 },
-      { "(data\n (flags oaep)\n (hash-algo sha1)\n (label \"test\")\n"
+      { GCRY_PK_RSA,
+        "(data\n (flags oaep)\n (hash-algo sha1)\n (label \"test\")\n"
        " (value #11223344556677889900AA#))\n",
        "(flags oaep)(hash-algo sha1)(label \"test\")",
        1,
        0,
        0 },
-      { "(data\n (flags oaep)\n (hash-algo sha1)\n (label \"test\")\n"
+      { GCRY_PK_RSA,
+        "(data\n (flags oaep)\n (hash-algo sha1)\n (label \"test\")\n"
        " (value #11223344556677889900AA#)\n"
         " (random-override #4253647587980912233445566778899019283747#))\n",
        "(flags oaep)(hash-algo sha1)(label \"test\")",
        1,
        0,
        0 },
-      {        "(data\n (flags )\n" " (value #11223344556677889900AA#))\n",
+      {        0,
+        "(data\n (flags )\n" " (value #11223344556677889900AA#))\n",
        NULL,
        1,
        0,
        0 },
-      {        "(data\n (flags )\n" " (value #0090223344556677889900AA#))\n",
+      {        0,
+        "(data\n (flags )\n" " (value #0090223344556677889900AA#))\n",
        NULL,
        1,
        0,
        0 },
-      { "(data\n (flags raw)\n" " (value #11223344556677889900AA#))\n",
+      { 0,
+        "(data\n (flags raw)\n" " (value #11223344556677889900AA#))\n",
        NULL,
        1,
        0,
        0 },
-      { "(data\n (flags pkcs1)\n"
+      { 0,
+        "(data\n (flags pkcs1)\n"
        " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n",
        NULL,
        0,
        GPG_ERR_CONFLICT,
        0},
-      { "(data\n (flags raw foo)\n"
+      { 0,
+        "(data\n (flags raw foo)\n"
        " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n",
        NULL,
        0,
        GPG_ERR_INV_FLAG,
        0},
-      { "(data\n (flags raw)\n"
+      { 0,
+        "(data\n (flags raw)\n"
        " (value #11223344556677889900AA#))\n",
        "(flags oaep)",
        1,
        0,
        GPG_ERR_ENCODING_PROBLEM },
-      { "(data\n (flags oaep)\n"
+      { GCRY_PK_RSA,
+        "(data\n (flags oaep)\n"
        " (value #11223344556677889900AA#))\n",
        "(flags pkcs1)",
        1,
        0,
        GPG_ERR_ENCODING_PROBLEM },
-      {        "(data\n (flags pss)\n"
+      {        0,
+        "(data\n (flags pss)\n"
        " (value #11223344556677889900AA#))\n",
        NULL,
        0,
        GPG_ERR_CONFLICT },
-      { NULL }
+      { 0, NULL }
     };
 
   (void)n;
 
   for (dataidx = 0; datas[dataidx].data; dataidx++)
     {
+      if (datas[dataidx].algo && datas[dataidx].algo != algo)
+       continue;
+
       if (verbose)
        fprintf (stderr, "  encryption/decryption test %d (algo %d)\n",
                  dataidx, algo);
index d394daf..c16f24e 100644 (file)
 #define DIMof(type,member)   DIM(((type *)0)->member)
 
 static int verbose;
+static int die_on_error;
 static int error_count;
 
+
 static void
 info (const char *format, ...)
 {
@@ -65,6 +67,8 @@ fail (const char *format, ...)
   vfprintf (stderr, format, arg_ptr);
   va_end (arg_ptr);
   error_count++;
+  if (die_on_error)
+    exit (1);
 }
 
 static void
@@ -136,12 +140,12 @@ extract_cmp_data (gcry_sexp_t sexp, const char *name, const char *expected,
   b = data_from_hex (expected, &blen);
   if (!a)
     {
-      fail ("%s: parameter \"%s\" missing in key\n", description, name);
+      info ("%s: parameter \"%s\" missing in key\n", description, name);
       rc = 1;
     }
   else if ( alen != blen || memcmp (a, b, alen) )
     {
-      fail ("%s: parameter \"%s\" does not match expected value\n",
+      info ("%s: parameter \"%s\" does not match expected value\n",
             description, name);
       rc = 1;
     }
@@ -856,7 +860,7 @@ check_oaep (void)
           "adf4cced1057cb758fc26aeefa441255ed4e64c199ee075e7f16646182fdb464"
           "739b68ab5daff0e63e9552016824f054bf4d3c8c90a97bb6b6553284eb429fcc"
         },{
-          "OAEPxample 10.2",
+          "OAEP Example 10.2",
           "e6ad181f053b58a904f2457510373e57",
           "6d17f5b4c1ffac351d195bf7b09d09f09a4079cf",
           "a2b1a430a9d657e2fa1c2bb5ed43ffb25c05a308fe9093c01031795f58744001"
@@ -930,7 +934,7 @@ check_oaep (void)
       size_t rsa_n_len, rsa_e_len, rsa_d_len;
       gcry_sexp_t sec_key, pub_key;
 
-      if (verbose)
+      if (verbose > 1)
         info ("(%s)\n", tbl[tno].desc);
 
       rsa_n = data_from_hex (tbl[tno].n, &rsa_n_len);
@@ -978,14 +982,17 @@ check_oaep (void)
           err = gcry_pk_encrypt (&ciph, plain, pub_key);
           if (err)
             {
-              fail ("gcry_pk_encrypt failed: %s\n", gpg_strerror (err));
               show_sexp ("plain:\n", ciph);
+              fail ("gcry_pk_encrypt failed: %s\n", gpg_strerror (err));
             }
           else
             {
               if (extract_cmp_data (ciph, "a", tbl[tno].m[mno].encr,
                                     tbl[tno].m[mno].desc))
-                show_sexp ("encrypt result:\n", ciph);
+                {
+                  show_sexp ("encrypt result:\n", ciph);
+                  fail ("mismatch in gcry_pk_encrypt\n");
+                }
               gcry_sexp_release (ciph);
               ciph = NULL;
             }
@@ -1010,14 +1017,17 @@ check_oaep (void)
           err = gcry_pk_decrypt (&plain, ciph, sec_key);
           if (err)
             {
-              fail ("gcry_pk_decrypt failed: %s\n", gpg_strerror (err));
               show_sexp ("ciph:\n", ciph);
+              fail ("gcry_pk_decrypt failed: %s\n", gpg_strerror (err));
             }
           else
             {
               if (extract_cmp_data (plain, "value", tbl[tno].m[mno].mesg,
                                     tbl[tno].m[mno].desc))
-                show_sexp ("decrypt result:\n", plain);
+                {
+                  show_sexp ("decrypt result:\n", plain);
+                  fail ("mismatch in gcry_pk_decrypt\n");
+                }
               gcry_sexp_release (plain);
               plain = NULL;
             }
@@ -1996,7 +2006,7 @@ check_pss (void)
       size_t rsa_n_len, rsa_e_len, rsa_d_len;
       gcry_sexp_t sec_key, pub_key;
 
-      if (verbose)
+      if (verbose > 1)
         info ("(%s)\n", tbl[tno].desc);
 
       rsa_n = data_from_hex (tbl[tno].n, &rsa_n_len);
@@ -2047,14 +2057,17 @@ check_pss (void)
           err = gcry_pk_sign (&sig, sigtmpl, sec_key);
           if (err)
             {
-              fail ("gcry_pk_sign failed: %s\n", gpg_strerror (err));
               show_sexp ("sigtmpl:\n", sigtmpl);
+              fail ("gcry_pk_sign failed: %s\n", gpg_strerror (err));
             }
           else
             {
               if (extract_cmp_data (sig, "s", tbl[tno].m[mno].sign,
                                     tbl[tno].m[mno].desc))
-                show_sexp ("encrypt result:\n", sig);
+                {
+                  show_sexp ("sign result:\n", sig);
+                  fail ("mismatch in gcry_pk_sign\n");
+                }
               gcry_sexp_release (sig);
               sig = NULL;
             }
@@ -2084,9 +2097,9 @@ check_pss (void)
           err = gcry_pk_verify (sig, sigtmpl, pub_key);
           if (err)
             {
-              fail ("gcry_pk_verify failed: %s\n", gpg_strerror (err));
               show_sexp ("sig:\n", sig);
               show_sexp ("sigtmpl:\n", sigtmpl);
+              fail ("gcry_pk_verify failed: %s\n", gpg_strerror (err));
             }
           gcry_sexp_release (sig);
           sig = NULL;
@@ -2099,19 +2112,58 @@ check_pss (void)
     }
 }
 
+
 int
 main (int argc, char **argv)
 {
+  int last_argc = -1;
   int debug = 0;
+  int run_oaep = 0;
+  int run_pss = 0;
 
-  if (argc > 1 && !strcmp (argv[1], "--verbose"))
-    verbose = 1;
-  else if (argc > 1 && !strcmp (argv[1], "--debug"))
+  if (argc)
+    { argc--; argv++; }
+
+  while (argc && last_argc != argc )
     {
-      verbose = 2;
-      debug = 1;
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose++;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--debug"))
+        {
+          verbose = 2;
+          debug = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--die"))
+        {
+          die_on_error = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--oaep"))
+        {
+          run_oaep = 1;
+          argc--; argv++;
+        }
+      else if (!strcmp (*argv, "--pss"))
+        {
+          run_pss = 1;
+          argc--; argv++;
+        }
     }
 
+  if (!run_oaep && !run_pss)
+    run_oaep = run_pss = 1;
+
+  gcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose);
   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
   if (!gcry_check_version ("1.5.0"))
     die ("version mismatch\n");
@@ -2121,12 +2173,13 @@ main (int argc, char **argv)
   /* No valuable keys are create, so we can speed up our RNG. */
   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
 
-
-  check_oaep ();
-  check_pss ();
+  if (run_oaep)
+    check_oaep ();
+  if (run_pss)
+    check_pss ();
 
   if (verbose)
-    fprintf (stderr, "\nAll tests completed. Errors: %i\n", error_count);
+    fprintf (stderr, "\nAll tests completed.  Errors: %i\n", error_count);
 
   return error_count ? 1 : 0;
 }