Merge branch 'master' into ueno-pss
authorWerner Koch <wk@gnupg.org>
Thu, 9 Jun 2011 07:05:15 +0000 (09:05 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 9 Jun 2011 07:05:15 +0000 (09:05 +0200)
Solved conflicts:
cipher/ChangeLog
cipher/pubkey.c
tests/ChangeLog
tests/basic.c

cipher/ChangeLog
cipher/pubkey.c
cipher/rsa.c
doc/gcrypt.texi
src/ChangeLog
src/cipher.h
tests/ChangeLog
tests/basic.c

index abe0cae..7bfbb5e 100644 (file)
@@ -1,3 +1,10 @@
+2011-06-08  Werner Koch  <wk@g10code.com>
+
+       * pubkey.c (pss_encode, pss_verify): Restructure and comment code
+       to match rfc-3447.  Replace secure allocs by plain allocs and
+       wipememory.  Use gcry_md_hash_buffer.
+       (octet_string_from_mpi): New.
+
 2011-06-03  Werner Koch  <wk@g10code.com>
 
        * pubkey.c (oaep_decode): Add more comments and restructure to
        * pubkey.c (gcry_pk_decrypt): Fix double-free when un-padding
        invalid data.  Thanks to Tom Ritter.
 
+2011-05-24  Daiki Ueno  <ueno@unixuser.org>
+
+       * rsa.c (rsa_verify): Use CMP if given, to check the decrypted
+       sig.
+
+       * pubkey.c (sexp_to_enc, sexp_data_to_mpi): Factor out
+       CTX initialization to ...
+       (init_encoding_ctx): .. new.
+       (gcry_pk_verify): Pass verify func and the arg to pubkey_verify.
+       (pss_encode, pss_verify, pss_verify_cmp): New.
+
 2011-05-23  Daiki Ueno  <ueno@unixuser.org>
 
        * pubkey.c (pkcs1_decode_for_encryption, oaep_decode): Fix memleak
index 1924318..b0d7d26 100644 (file)
@@ -1393,6 +1393,361 @@ oaep_decode (unsigned char **r_result, size_t *r_resultlen,
 }
 
 
+/* RFC-3447 (pkcs#1 v2.1) PSS encoding.  Encode {VALUE,VALUELEN} for
+   an NBITS key.  ALGO is a valid hash algorithm and SALTLEN is the
+   length of salt to be used.  On success the result is stored as a
+   new MPI at R_RESULT.  On error the value at R_RESULT is undefined.
+
+   Here is figure 2 from the RFC (errata 595 applied) depicting the
+   process:
+
+                                  +-----------+
+                                  |     M     |
+                                  +-----------+
+                                        |
+                                        V
+                                      Hash
+                                        |
+                                        V
+                          +--------+----------+----------+
+                     M' = |Padding1|  mHash   |   salt   |
+                          +--------+----------+----------+
+                                         |
+               +--------+----------+     V
+         DB =  |Padding2| salt     |   Hash
+               +--------+----------+     |
+                         |               |
+                         V               |    +----+
+                        xor <--- MGF <---|    |0xbc|
+                         |               |    +----+
+                         |               |      |
+                         V               V      V
+               +-------------------+----------+----+
+         EM =  |    maskedDB       |     H    |0xbc|
+               +-------------------+----------+----+
+
+  */
+static gcry_err_code_t
+pss_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo,
+           const unsigned char *value, size_t valuelen,
+           int saltlen)
+{
+  gcry_err_code_t rc = 0;
+  gcry_error_t err;
+  size_t hlen;                 /* Length of the hash digest.  */
+  unsigned char *em = NULL;    /* Encoded message.  */
+  size_t emlen = (nbits+7)/8;  /* Length in bytes of EM.  */
+  unsigned char *h;            /* Points into EM.  */
+  unsigned char *buf = NULL;   /* Help buffer.  */
+  size_t buflen;               /* Length of BUF.  */
+  unsigned char *mhash;        /* Points into BUF.  */
+  unsigned char *salt;         /* Points into BUF.  */
+  unsigned char *dbmask;       /* Points into BUF.  */
+  unsigned char *p;
+  size_t n;
+
+  /* This code is implemented as described by rfc-3447 9.1.1.  */
+
+  /* Get the length of the digest.  */
+  hlen = gcry_md_get_algo_dlen (algo);
+  gcry_assert (hlen);  /* We expect a valid ALGO here.  */
+
+  /* Allocate a help buffer and setup some pointers.  */
+  buflen = 8 + hlen + saltlen + (emlen - hlen - 1);
+  buf = gcry_malloc (buflen);
+  if (!buf)
+    {
+      rc = gpg_err_code_from_syserror ();
+      goto leave;
+    }
+  mhash = buf + 8;
+  salt  = mhash + hlen;
+  dbmask= salt + saltlen;
+
+  /* Step 2: mHash = Hash(M).  */
+  /* Fixme: We should not do that but use VALUE directly as the hash.  */
+  gcry_md_hash_buffer (algo, mhash, value, valuelen);
+
+  /* Step 3: Check length constraints.  */
+  if (emlen < hlen + saltlen + 2)
+    {
+      rc = GPG_ERR_TOO_SHORT;
+      goto leave;
+    }
+
+  /* Allocate space for EM.  */
+  em = gcry_malloc (emlen);
+  if (!em)
+    {
+      rc = gpg_err_code_from_syserror ();
+      goto leave;
+    }
+  h = em + emlen - 1 - hlen;
+
+  /* Step 4: Create a salt.  */
+  if (saltlen)
+    gcry_randomize (salt, saltlen, GCRY_STRONG_RANDOM);
+
+  /* Step 5 and 6: M' = Hash(Padding1 || mHash || salt).  */
+  memset (buf, 0, 8);  /* Padding.  */
+  gcry_md_hash_buffer (algo, h, buf, 8 + hlen + saltlen);
+
+  /* Step 7 and 8: DB = PS || 0x01 || salt.  */
+  /* Note that we use EM to store DB and later Xor in-place.  */
+  p = em + emlen - 1 - hlen - saltlen - 1;
+  memset (em, 0, p - em);
+  *p++ = 0x01;
+  memcpy (p, salt, saltlen);
+
+  /* Step 9: dbmask = MGF(H, emlen - hlen - 1).  */
+  mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo);
+
+  /* Step 10: maskedDB = DB ^ dbMask */
+  for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++)
+    em[n] ^= *p;
+
+  /* Step 11: Set the leftmost bits to zero.  */
+  em[0] &= 0xFF >> (8 * emlen - nbits);
+
+  /* Step 12: EM = maskedDB || H || 0xbc.  */
+  em[emlen-1] = 0xbc;
+
+  /* Convert EM into an MPI.  */
+  err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, em, emlen, NULL);
+  if (err)
+    rc = gcry_err_code (err);
+  else if (DBG_CIPHER)
+    log_mpidump ("PSS encoded data", *r_result);
+
+ leave:
+  if (em)
+    {
+      wipememory (em, emlen);
+      gcry_free (em);
+    }
+  if (buf)
+    {
+      wipememory (buf, buflen);
+      gcry_free (buf);
+    }
+  return rc;
+}
+
+
+/* Turn VALUE into an octet string and store that at R_FRAME.  If that
+   octet string is shorter than NBYTES pad it to the left with zeroes.
+   If VALUE does not fit into NBYTES return an error code. */
+static gpg_err_code_t
+octet_string_from_mpi (unsigned char **r_frame, gcry_mpi_t value, size_t nbytes)
+{
+  gpg_err_code_t rc;
+  size_t nframe, noff, n;
+  unsigned char *frame;
+
+  *r_frame = NULL;
+
+  rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG,
+                                      NULL, 0, &nframe, value));
+  if (rc)
+    return rc;
+  if (nframe > nbytes)
+    return GPG_ERR_TOO_LARGE; /* Value too long to fit into NBYTES.  */
+
+  noff = (nframe < nbytes)? nbytes - nframe : 0;
+  n = nframe + noff;
+  frame = mpi_is_secure (value)? gcry_malloc_secure (n) : gcry_malloc (n);
+  if (!frame)
+    {
+      rc = gpg_err_code_from_syserror ();
+      return rc;
+    }
+  if (noff)
+    memset (frame, 0, noff);
+  nframe += noff;
+  rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG,
+                                      frame+noff, nframe-noff, NULL, value));
+  if (rc)
+    {
+      gcry_free (frame);
+      return rc;
+    }
+
+  *r_frame = frame;
+  return 0;
+}
+
+
+/* Verify a signature assuming PSS padding.  VALUE is the hash of the
+   message.  ENCODED is the output of the RSA public key function.
+   NBITS is the size of the secret key.  ALGO is a hash algorithm and
+   SALTLEN is the length of the used salt.  The function returns 0 on
+   success or on error code otherwise.  */
+static gcry_err_code_t
+pss_verify (gcry_mpi_t value, gcry_mpi_t encoded, unsigned int nbits, int algo,
+           size_t saltlen)
+{
+  gcry_err_code_t rc = 0;
+  size_t hlen;                 /* Length of the hash digest.  */
+  unsigned char *em = NULL;    /* Encoded message.  */
+  size_t emlen = (nbits+7)/8;  /* Length in bytes of EM.  */
+  unsigned char *salt;         /* Points into EM.  */
+  unsigned char *h;            /* Points into EM.  */
+  unsigned char *buf = NULL;   /* Help buffer.  */
+  size_t buflen;               /* Length of BUF.  */
+  unsigned char *dbmask;       /* Points into BUF.  */
+  unsigned char *mhash;        /* Points into BUF.  */
+  unsigned char *p;
+  size_t n;
+
+  /* This code is implemented as described by rfc-3447 9.1.2.  */
+
+  /* Get the length of the digest.  */
+  hlen = gcry_md_get_algo_dlen (algo);
+  gcry_assert (hlen);  /* We expect a valid ALGO here.  */
+
+  /* Allocate a help buffer and setup some pointers.
+     This buffer is used for two purposes:
+        +------------------------------+-------+
+     1. | dbmask                       | mHash |
+        +------------------------------+-------+
+           emlen - hlen - 1              hlen
+
+        +----------+-------+---------+-+-------+
+     2. | padding1 | mHash | salt    | | mHash |
+        +----------+-------+---------+-+-------+
+             8       hlen    saltlen     hlen
+  */
+  buflen = 8 + hlen + saltlen;
+  if (buflen < emlen - hlen - 1)
+    buflen = emlen - hlen - 1;
+  buflen += hlen;
+  buf = gcry_malloc (buflen);
+  if (!buf)
+    {
+      rc = gpg_err_code_from_syserror ();
+      goto leave;
+    }
+  dbmask = buf;
+  mhash = buf + buflen - hlen;
+
+  /* Convert the value into an octet string.  */
+  /* rc = octet_string_from_mpi (&em, value, emlen); */
+  /* if (rc) */
+  /*   goto leave; */
+
+  em = gcry_malloc (emlen);
+  if (!em)
+    {
+      rc = gpg_err_code_from_syserror ();
+      goto leave;
+    }
+  rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG, em, emlen, &n, value));
+  if (rc)
+    goto leave;
+
+  /* Step 2: mHash = Hash(M).  */
+  /* Fixme: We should not do that but use VALUE directly as the hash.  */
+  gcry_md_hash_buffer (algo, mhash, em, n);
+  gcry_free (em);
+  em = NULL;
+
+  /* Convert the signature into an octet string.  */
+  rc = octet_string_from_mpi (&em, encoded, emlen);
+  if (rc)
+    goto leave;
+
+  /* Step 3: Check length of EM.  Because we internally use MPI
+     functions we can't do this properly; EMLEN is always the length
+     of the key because octet_string_from_mpi needs to left pad the
+     result with zero to cope with the fact that our MPIs suppress all
+     leading zeroes.  Thus what we test here are merely the digest and
+     salt lengths to the key.  */
+  if (emlen < hlen + saltlen + 2)
+    {
+      rc = GPG_ERR_TOO_SHORT; /* For the hash and saltlen.  */
+      goto leave;
+    }
+
+  /* Step 4: Check last octet.  */
+  if (em[emlen - 1] != 0xbc)
+    {
+      rc = GPG_ERR_BAD_SIGNATURE;
+      goto leave;
+    }
+
+  /* Step 5: Split EM.  */
+  h = em + emlen - 1 - hlen;
+
+  /* Step 6: Check the leftmost bits.  */
+  if ((em[0] & ~(0xFF >> (8 * emlen - nbits))))
+    {
+      rc = GPG_ERR_BAD_SIGNATURE;
+      goto leave;
+    }
+
+  /* Step 7: dbmask = MGF(H, emlen - hlen - 1).  */
+  mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo);
+
+  /* Step 8: maskedDB = DB ^ dbMask.  */
+  for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++)
+    em[n] ^= *p;
+
+  /* Step 9: Set leftmost bits in DB to zero.  */
+  em[0] &= 0xFF >> (8 * emlen - nbits);
+
+  /* Step 10: Check the padding of DB.  */
+  for (n = 0; n < emlen - hlen - saltlen - 2 && !em[n]; n++)
+    ;
+  if (n != emlen - hlen - saltlen - 2 || em[n++] != 1)
+    {
+      rc = GPG_ERR_BAD_SIGNATURE;
+      goto leave;
+    }
+
+  /* Step 11: Extract salt from DB.  */
+  salt = em + n;
+
+  /* Step 12:  M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
+  memset (buf, 0, 8);
+  memcpy (buf+8, mhash, hlen);
+  memcpy (buf+8+hlen, salt, saltlen);
+
+  /* Step 13:  H' = Hash(M').  */
+  gcry_md_hash_buffer (algo, buf, buf, 8 + hlen + saltlen);
+
+  /* Step 14:  Check H == H'.   */
+  rc = memcmp (h, buf, hlen) ? GPG_ERR_BAD_SIGNATURE : GPG_ERR_NO_ERROR;
+
+ leave:
+  if (em)
+    {
+      wipememory (em, emlen);
+      gcry_free (em);
+    }
+  if (buf)
+    {
+      wipememory (buf, buflen);
+      gcry_free (buf);
+    }
+  return rc;
+}
+
+
+/* Callback for the pubkey algorithm code to verify PSS signatures.
+   OPAQUE is the data provided by the actual caller.  The meaning of
+   TMP depends on the actual algorithm (but there is only RSA); now
+   for RSA it is the output of running the public key function on the
+   input.  */
+static int
+pss_verify_cmp (void *opaque, gcry_mpi_t tmp)
+{
+  struct pk_encoding_ctx *ctx = opaque;
+  gcry_mpi_t hash = ctx->verify_arg;
+
+  return pss_verify (hash, tmp, ctx->nbits - 1, ctx->hash_algo, ctx->saltlen);
+}
+
+
 /* Internal function.   */
 static gcry_err_code_t
 sexp_elements_extract (gcry_sexp_t key_sexp, const char *element_names,
@@ -1852,17 +2207,9 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo,
   int parsed_flags = 0;
   const char *elems;
   gcry_mpi_t *array = NULL;
-  struct pk_encoding_ctx dummy_ctx;
 
   *ret_modern = 0;
 
-  if (!ctx)
-    ctx = &dummy_ctx;
-  ctx->encoding = PUBKEY_ENC_RAW;
-  ctx->hash_algo = GCRY_MD_SHA1;
-  ctx->label = NULL;
-  ctx->labellen = 0;
-
   /* Check that the first element is valid.  */
   list = gcry_sexp_find_token (sexp, "enc-val" , 0);
   if (!list)
@@ -1899,14 +2246,20 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo,
           if (! s)
             ; /* Not a data element - ignore.  */
           else if (n == 3 && !memcmp (s, "raw", 3)
-                   && ctx->encoding == PUBKEY_ENC_RAW)
-            ; /* This is just a dummy as it is the default.  */
+                   && ctx->encoding == PUBKEY_ENC_UNKNOWN)
+            ctx->encoding = PUBKEY_ENC_RAW;
           else if (n == 5 && !memcmp (s, "pkcs1", 5)
-                   && ctx->encoding == PUBKEY_ENC_RAW)
+                   && ctx->encoding == PUBKEY_ENC_UNKNOWN)
            ctx->encoding = PUBKEY_ENC_PKCS1;
           else if (n == 4 && !memcmp (s, "oaep", 4)
-                   && ctx->encoding == PUBKEY_ENC_RAW)
+                   && ctx->encoding == PUBKEY_ENC_UNKNOWN)
            ctx->encoding = PUBKEY_ENC_OAEP;
+          else if (n == 3 && !memcmp (s, "pss", 3)
+                   && ctx->encoding == PUBKEY_ENC_UNKNOWN)
+           {
+             err = GPG_ERR_CONFLICT;
+             goto leave;
+           }
           else if (n == 11 && ! memcmp (s, "no-blinding", 11))
             parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
           else
@@ -2046,11 +2399,12 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo,
    (<mpi>)
    or
    (data
-    [(flags [raw, pkcs1, oaep, no-blinding])]
+    [(flags [raw, pkcs1, oaep, pss, no-blinding])]
     [(hash <algo> <value>)]
     [(value <text>)]
     [(hash-algo <algo>)]
     [(label <label>)]
+    [(salt-length <length>)]
    )
 
    Either the VALUE or the HASH element must be present for use
@@ -2058,12 +2412,12 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo,
 
    HASH-ALGO and LABEL are specific to OAEP.
 
-   NBITS is the length of the key in bits.
+   SALT-LENGTH is for PSS.
 
 */
 static gcry_err_code_t
-sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
-                  int for_encryption, int *flags, struct pk_encoding_ctx *ctx)
+sexp_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
+                 struct pk_encoding_ctx *ctx)
 {
   gcry_err_code_t rc = 0;
   gcry_sexp_t ldata, lhash, lvalue;
@@ -2071,19 +2425,7 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
   size_t n;
   const char *s;
   int unknown_flag=0;
-  int parsed_flags = 0, dummy_flags;
-  struct pk_encoding_ctx dummy_ctx;
-
-  if (! flags)
-    flags = &dummy_flags;
-
-  if (! ctx)
-    ctx = &dummy_ctx;
-
-  ctx->encoding = PUBKEY_ENC_UNKNOWN;
-  ctx->hash_algo = GCRY_MD_SHA1;
-  ctx->label = NULL;
-  ctx->labellen = 0;
+  int parsed_flags = 0;
 
   *ret_mpi = NULL;
   ldata = gcry_sexp_find_token (input, "data", 0);
@@ -2112,6 +2454,9 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
             else if ( n == 4 && !memcmp (s, "oaep", 4)
                       && ctx->encoding == PUBKEY_ENC_UNKNOWN)
               ctx->encoding = PUBKEY_ENC_OAEP;
+            else if ( n == 3 && !memcmp (s, "pss", 3)
+                      && ctx->encoding == PUBKEY_ENC_UNKNOWN)
+              ctx->encoding = PUBKEY_ENC_PSS;
            else if (n == 11 && ! memcmp (s, "no-blinding", 11))
              parsed_flags |= PUBKEY_FLAG_NO_BLINDING;
             else
@@ -2138,7 +2483,8 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
       if (!*ret_mpi)
         rc = GPG_ERR_INV_OBJ;
     }
-  else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue && for_encryption)
+  else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue
+          && ctx->op == PUBKEY_OP_ENCRYPT)
     {
       const void * value;
       size_t valuelen;
@@ -2146,9 +2492,10 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
       if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen )
         rc = GPG_ERR_INV_OBJ;
       else
-       rc = pkcs1_encode_for_encryption (ret_mpi, nbits, value, valuelen);
+       rc = pkcs1_encode_for_encryption (ret_mpi, ctx->nbits, value, valuelen);
     }
-  else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lhash && !for_encryption)
+  else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lhash
+          && (ctx->op == PUBKEY_OP_SIGN || ctx->op == PUBKEY_OP_VERIFY))
     {
       if (gcry_sexp_length (lhash) != 3)
         rc = GPG_ERR_INV_OBJ;
@@ -2156,23 +2503,24 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
         rc = GPG_ERR_INV_OBJ;
       else
         {
-          int algo;
           const void * value;
           size_t valuelen;
 
-         algo = get_hash_algo (s, n);
+         ctx->hash_algo = get_hash_algo (s, n);
 
-          if (!algo)
+          if (!ctx->hash_algo)
             rc = GPG_ERR_DIGEST_ALGO;
           else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen))
                     || !valuelen )
             rc = GPG_ERR_INV_OBJ;
           else
-           rc = pkcs1_encode_for_signature (ret_mpi, nbits, value, valuelen,
-                                            algo);
+           rc = pkcs1_encode_for_signature (ret_mpi, ctx->nbits,
+                                            value, valuelen,
+                                            ctx->hash_algo);
         }
     }
-  else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue && for_encryption)
+  else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue
+          && ctx->op == PUBKEY_OP_ENCRYPT)
     {
       const void * value;
       size_t valuelen;
@@ -2224,10 +2572,76 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
                goto leave;
            }
 
-         rc = oaep_encode (ret_mpi, nbits, ctx->hash_algo, value, valuelen,
+         rc = oaep_encode (ret_mpi, ctx->nbits, ctx->hash_algo,
+                           value, valuelen,
                            ctx->label, ctx->labellen);
        }
     }
+  else if (ctx->encoding == PUBKEY_ENC_PSS && lhash
+          && ctx->op == PUBKEY_OP_SIGN)
+    {
+      if (gcry_sexp_length (lhash) != 3)
+        rc = GPG_ERR_INV_OBJ;
+      else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n )
+        rc = GPG_ERR_INV_OBJ;
+      else
+        {
+          const void * value;
+          size_t valuelen;
+
+         ctx->hash_algo = get_hash_algo (s, n);
+
+          if (!ctx->hash_algo)
+            rc = GPG_ERR_DIGEST_ALGO;
+          else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen))
+                    || !valuelen )
+            rc = GPG_ERR_INV_OBJ;
+          else
+           {
+             gcry_sexp_t list;
+
+             /* Get SALT-LENGTH. */
+             list = gcry_sexp_find_token (ldata, "salt-length", 0);
+             if (list)
+               {
+                 s = gcry_sexp_nth_data (list, 1, &n);
+                 if (!s)
+                   {
+                     rc = GPG_ERR_NO_OBJ;
+                     goto leave;
+                   }
+                 ctx->saltlen = (unsigned int)strtoul (s, NULL, 10);
+                 gcry_sexp_release (list);
+               }
+             rc = pss_encode (ret_mpi, ctx->nbits - 1, ctx->hash_algo,
+                              value, valuelen,
+                              ctx->saltlen);
+           }
+        }
+    }
+  else if (ctx->encoding == PUBKEY_ENC_PSS && lhash
+          && ctx->op == PUBKEY_OP_VERIFY)
+    {
+      if (gcry_sexp_length (lhash) != 3)
+        rc = GPG_ERR_INV_OBJ;
+      else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n )
+        rc = GPG_ERR_INV_OBJ;
+      else
+        {
+         ctx->hash_algo = get_hash_algo (s, n);
+
+          if (!ctx->hash_algo)
+            rc = GPG_ERR_DIGEST_ALGO;
+         else
+           {
+             *ret_mpi = gcry_sexp_nth_mpi (lhash, 2, 0);
+             if (!*ret_mpi)
+               rc = GPG_ERR_INV_OBJ;
+             ctx->verify_cmp = pss_verify_cmp;
+             ctx->verify_arg = *ret_mpi;
+           }
+       }
+    }
   else
     rc = GPG_ERR_CONFLICT;
 
@@ -2237,7 +2651,7 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
   gcry_sexp_release (lvalue);
 
   if (!rc)
-    *flags = parsed_flags;
+    ctx->flags = parsed_flags;
   else
     {
       gcry_free (ctx->label);
@@ -2247,6 +2661,22 @@ sexp_data_to_mpi (gcry_sexp_t input, unsigned int nbits, gcry_mpi_t *ret_mpi,
   return rc;
 }
 
+static void
+init_encoding_ctx (struct pk_encoding_ctx *ctx, enum pk_operation op,
+                  unsigned int nbits)
+{
+  ctx->op = op;
+  ctx->nbits = nbits;
+  ctx->encoding = PUBKEY_ENC_UNKNOWN;
+  ctx->flags = 0;
+  ctx->hash_algo = GCRY_MD_SHA1;
+  ctx->label = NULL;
+  ctx->labellen = 0;
+  ctx->saltlen = 20;
+  ctx->verify_cmp = NULL;
+  ctx->verify_arg = NULL;
+}
+
 
 /*
    Do a PK encrypt operation
@@ -2275,7 +2705,6 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey)
 {
   gcry_mpi_t *pkey = NULL, data = NULL, *ciph = NULL;
   const char *algo_name, *algo_elems;
-  int flags;
   struct pk_encoding_ctx ctx;
   gcry_err_code_t rc;
   gcry_pk_spec_t *pubkey = NULL;
@@ -2285,7 +2714,6 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey)
 
   REGISTER_DEFAULT_PUBKEYS;
 
-  memset (&ctx, 0, sizeof(struct pk_encoding_ctx));
   /* Get the key. */
   rc = sexp_to_key (s_pkey, 0, NULL, &pkey, &module);
   if (rc)
@@ -2306,8 +2734,8 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey)
   algo_elems = pubkey->elements_enc;
 
   /* Get the stuff we want to encrypt. */
-  rc = sexp_data_to_mpi (s_data, gcry_pk_get_nbits (s_pkey), &data, 1,
-                         &flags, &ctx);
+  init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT, gcry_pk_get_nbits (s_pkey));
+  rc = sexp_data_to_mpi (s_data, &data, &ctx);
   if (rc)
     goto leave;
 
@@ -2318,7 +2746,7 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey)
       rc = gpg_err_code_from_syserror ();
       goto leave;
     }
-  rc = pubkey_encrypt (module->mod_id, ciph, data, pkey, flags);
+  rc = pubkey_encrypt (module->mod_id, ciph, data, pkey, ctx.flags);
   mpi_free (data);
   data = NULL;
   if (rc)
@@ -2443,6 +2871,7 @@ gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey)
   if (rc)
     goto leave;
 
+  init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT, gcry_pk_get_nbits (s_skey));
   rc = sexp_to_enc (s_data, &data, &module_enc, &modern, &flags, &ctx);
   if (rc)
     goto leave;
@@ -2558,6 +2987,7 @@ gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey)
   gcry_pk_spec_t *pubkey = NULL;
   gcry_module_t module = NULL;
   const char *algo_name, *algo_elems;
+  struct pk_encoding_ctx ctx;
   int i;
   gcry_err_code_t rc;
 
@@ -2579,8 +3009,8 @@ gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey)
 
   /* Get the stuff we want to sign.  Note that pk_get_nbits does also
       work on a private key. */
-  rc = sexp_data_to_mpi (s_hash, gcry_pk_get_nbits (s_skey),
-                             &hash, 0, NULL, NULL);
+  init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, gcry_pk_get_nbits (s_skey));
+  rc = sexp_data_to_mpi (s_hash, &hash, &ctx);
   if (rc)
     goto leave;
 
@@ -2670,6 +3100,7 @@ gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey)
 {
   gcry_module_t module_key = NULL, module_sig = NULL;
   gcry_mpi_t *pkey = NULL, hash = NULL, *sig = NULL;
+  struct pk_encoding_ctx ctx;
   gcry_err_code_t rc;
 
   REGISTER_DEFAULT_PUBKEYS;
@@ -2691,11 +3122,14 @@ gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey)
       goto leave;
     }
 
-  rc = sexp_data_to_mpi (s_hash, gcry_pk_get_nbits (s_pkey), &hash, 0, 0, NULL);
+  /* Get the stuff we want to verify. */
+  init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, gcry_pk_get_nbits (s_pkey));
+  rc = sexp_data_to_mpi (s_hash, &hash, &ctx);
   if (rc)
     goto leave;
 
-  rc = pubkey_verify (module_key->mod_id, hash, sig, pkey, NULL, NULL);
+  rc = pubkey_verify (module_key->mod_id, hash, sig, pkey,
+                     ctx.verify_cmp, &ctx);
 
  leave:
   if (pkey)
index 1973280..ccc9f96 100644 (file)
@@ -1036,8 +1036,10 @@ rsa_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
       log_mpidump ("             hash:", hash );
     }
 #endif /*IS_DEVELOPMENT_VERSION*/
-  /*rc = (*cmp)( opaquev, result );*/
-  rc = mpi_cmp (result, hash) ? GPG_ERR_BAD_SIGNATURE : GPG_ERR_NO_ERROR;
+  if (cmp)
+    rc = (*cmp) (opaquev, result);
+  else
+    rc = mpi_cmp (result, hash) ? GPG_ERR_BAD_SIGNATURE : GPG_ERR_NO_ERROR;
   gcry_mpi_release (result);
 
   return rc;
index 8bafc31..b3dc127 100644 (file)
@@ -2327,9 +2327,12 @@ sub-S-expression named `flags'; the following flags are known:
 
 @table @code
 @item pkcs1
-Use PKCS#1 block type 2 padding.
+Use PKCS#1 block type 2 padding for encryption, block type 1 padding
+for signing.
 @item oaep
-Use RSA-OAEP padding.
+Use RSA-OAEP padding for encryption.
+@item pss
+Use RSA-PSS padding for signing.
 @item no-blinding
 Do not use a technique called `blinding', which is used by default in
 order to prevent leaking of secret information.  Blinding is only
index 23601f0..aaaf9a4 100644 (file)
@@ -1,3 +1,9 @@
+2011-05-24  Daiki Ueno  <ueno@unixuser.org>
+
+       * cipher.h (pk_operation): New.
+       (pk_encoding_ctx): Add new fields: op, nbits, flags, verify_cmp,
+       and verify_arg.
+
 2011-05-19  Daiki Ueno  <ueno@unixuser.org>
 
        * Makefile.am (gcryptrnd_LDADD): Supply $(GPG_ERROR_LIBS) for
index 579e57e..0f923d7 100644 (file)
 
 #define PUBKEY_FLAG_NO_BLINDING    (1 << 0)
 
+enum pk_operation
+  {
+    PUBKEY_OP_ENCRYPT,
+    PUBKEY_OP_DECRYPT,
+    PUBKEY_OP_SIGN,
+    PUBKEY_OP_VERIFY
+  };
+
 enum pk_encoding
   {
     PUBKEY_ENC_RAW,
     PUBKEY_ENC_PKCS1,
     PUBKEY_ENC_OAEP,
+    PUBKEY_ENC_PSS,
     PUBKEY_ENC_UNKNOWN
   };
 
 struct pk_encoding_ctx
 {
+  enum pk_operation op;
+  unsigned int nbits;
+
   enum pk_encoding encoding;
+  int flags;
+
   int hash_algo;
+
+  /* for OAEP */
   unsigned char *label;
   size_t labellen;
+
+  /* for PSS */
+  size_t saltlen;
+
+  int (* verify_cmp) (void *opaque, gcry_mpi_t tmp);
+  void *verify_arg;
 };
 
 #define CIPHER_INFO_NO_WEAK_KEY    1
index c294806..d70c963 100644 (file)
@@ -6,6 +6,16 @@
 
        * basic.c (check_pubkey_crypt): Add test data with invalid padding.
 
+2011-05-24  Daiki Ueno  <ueno@unixuser.org>
+
+       * basic.c (do_check_one_pubkey): Add new arg ALGO to tell which PK
+       algorithm is used for check_pubkey_sign, check_pubkey_crypt,
+       check_pubkey_grip.
+       (check_pubkey_sign): Add new arg ALGO; skip test data if it does
+       not match ALGO.
+       (check_pubkey_crypt): Add new arg ALGO.
+       (check_pubkey_grip): Ditto.
+
 2011-05-18  Daiki Ueno  <ueno@unixuser.org>
 
        * basic.c (check_pubkey_crypt): Remove unused "unpad" flag.
index c8f930a..0c66938 100644 (file)
@@ -2281,7 +2281,7 @@ verify_one_signature (gcry_sexp_t pkey, gcry_sexp_t hash,
 /* Test the public key sign function using the private ket SKEY. PKEY
    is used for verification. */
 static void
-check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
+check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
 {
   gcry_error_t rc;
   gcry_sexp_t sig, badhash, hash;
@@ -2292,39 +2292,54 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
   static struct
   {
     const char *data;
+    int algo;
     int expected_rc;
   } datas[] =
     {
       { "(data\n (flags pkcs1)\n"
        " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n",
+       0,
        0 },
       { "(data\n (flags oaep)\n"
        " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n",
+       0,
        GPG_ERR_CONFLICT },
       /* This test is to see whether hash algorithms not hard wired in
          pubkey.c are detected:  */
       { "(data\n (flags pkcs1)\n"
        " (hash oid.1.3.14.3.2.29 "
         "       #11223344556677889900AABBCCDDEEFF10203040#))\n",
+       0,
        0 },
       {        "(data\n (flags )\n"
        " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n",
+       0,
        GPG_ERR_CONFLICT },
       {        "(data\n (flags pkcs1)\n"
        " (hash foo #11223344556677889900AABBCCDDEEFF10203040#))\n",
+       0,
        GPG_ERR_DIGEST_ALGO },
       {        "(data\n (flags )\n" " (value #11223344556677889900AA#))\n",
+       0,
        0 },
       {        "(data\n (flags )\n" " (value #0090223344556677889900AA#))\n",
+       0,
        0 },
       { "(data\n (flags raw)\n" " (value #11223344556677889900AA#))\n",
+       0,
        0 },
       {        "(data\n (flags pkcs1)\n"
        " (value #11223344556677889900AA#))\n",
+       0,
        GPG_ERR_CONFLICT },
       { "(data\n (flags raw foo)\n"
        " (value #11223344556677889900AA#))\n",
+       0,
        GPG_ERR_INV_FLAG },
+      { "(data\n (flags pss)\n"
+       " (hash sha1 #11223344556677889900AABBCCDDEEFF10203040#))\n",
+       GCRY_PK_RSA,
+       0 },
       { NULL }
     };
 
@@ -2336,6 +2351,9 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
 
   for (dataidx = 0; datas[dataidx].data; dataidx++)
     {
+      if (datas[dataidx].algo && datas[dataidx].algo != algo)
+       continue;
+
       if (verbose)
        fprintf (stderr, "  signature test %d\n", dataidx);
 
@@ -2361,7 +2379,7 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
 }
 
 static void
-check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
+check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
 {
   gcry_error_t rc;
   gcry_sexp_t plain, ciph, data;
@@ -2444,6 +2462,11 @@ check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
        1,
        0,
        GPG_ERR_ENCODING_PROBLEM },
+      {        "(data\n (flags pss)\n"
+       " (value #11223344556677889900AA#))\n",
+       NULL,
+       0,
+       GPG_ERR_CONFLICT },
       { NULL }
     };
 
@@ -2536,7 +2559,7 @@ check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
 
 static void
 check_pubkey_grip (int n, const unsigned char *grip,
-                  gcry_sexp_t skey, gcry_sexp_t pkey)
+                  gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
 {
   unsigned char sgrip[20], pgrip[20];
 
@@ -2552,14 +2575,14 @@ check_pubkey_grip (int n, const unsigned char *grip,
 
 static void
 do_check_one_pubkey (int n, gcry_sexp_t skey, gcry_sexp_t pkey,
-                    const unsigned char *grip, int flags)
+                    const unsigned char *grip, int algo, int flags)
 {
  if (flags & FLAG_SIGN)
-    check_pubkey_sign (n, skey, pkey);
+   check_pubkey_sign (n, skey, pkey, algo);
  if (flags & FLAG_CRYPT)
-   check_pubkey_crypt (n, skey, pkey);
+   check_pubkey_crypt (n, skey, pkey, algo);
  if (grip && (flags & FLAG_GRIP))
-   check_pubkey_grip (n, grip, skey, pkey);
+   check_pubkey_grip (n, grip, skey, pkey, algo);
 }
 
 static void
@@ -2577,7 +2600,8 @@ check_one_pubkey (int n, test_spec_pubkey_t spec)
     die ("converting sample key failed: %s\n", gpg_strerror (err));
 
   do_check_one_pubkey (n, skey, pkey,
-                       (const unsigned char*)spec.key.grip, spec.flags);
+                       (const unsigned char*)spec.key.grip,
+                      spec.id, spec.flags);
 
   gcry_sexp_release (skey);
   gcry_sexp_release (pkey);
@@ -2620,7 +2644,8 @@ check_one_pubkey_new (int n)
   gcry_sexp_t skey, pkey;
 
   get_keys_new (&pkey, &skey);
-  do_check_one_pubkey (n, skey, pkey, NULL, FLAG_SIGN | FLAG_CRYPT);
+  do_check_one_pubkey (n, skey, pkey, NULL,
+                      GCRY_PK_RSA, FLAG_SIGN | FLAG_CRYPT);
   gcry_sexp_release (pkey);
   gcry_sexp_release (skey);
 }