[PATCH] Add a simple PKCS#1 padding mode

Dmitry Eremin-Solenikov dbaryshkov at gmail.com
Mon Dec 16 17:40:44 CET 2013


* cipher/rsa-common.c (_gcry_rsa_pkcs1_encode_simple_for_sig):
  PKCS#1-encode data with embedded hash OID for signature verification.
* cipher/pubkey-util.c (_gcry_pk_util_data_to_mpi):
  handle s-exp like (data (flags pkcs1) (value xxxxx))
* tests/basic.c (check_pubkey_sign): fix the return value for this kind
  of s-exp.

--
Allow user to specify (flags pkcs1) to enable pkcs1 padding of raw value
(no hash algorithm is specified). It is up to the user to verify that
passed value is properly formatted and includes DER-encoded ASN OID of
the hash function.

This is required to enable GnuTLS to use gcrypt library as a crypto backend.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>
---
 cipher/pubkey-internal.h |  4 +++
 cipher/pubkey-util.c     | 15 +++++++++++
 cipher/rsa-common.c      | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/basic.c            |  2 +-
 4 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/cipher/pubkey-internal.h b/cipher/pubkey-internal.h
index db1399d..4b5ee90 100644
--- a/cipher/pubkey-internal.h
+++ b/cipher/pubkey-internal.h
@@ -56,6 +56,10 @@ gpg_err_code_t
 _gcry_rsa_pkcs1_decode_for_enc (unsigned char **r_result, size_t *r_resultlen,
                                 unsigned int nbits, gcry_mpi_t value);
 gpg_err_code_t
+_gcry_rsa_pkcs1_encode_simple_for_sig (gcry_mpi_t *r_result, unsigned int nbits,
+                                const unsigned char *value, size_t valuelen);
+
+gpg_err_code_t
 _gcry_rsa_pkcs1_encode_for_sig (gcry_mpi_t *r_result, unsigned int nbits,
                                 const unsigned char *value, size_t valuelen,
                                 int algo);
diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c
index 616b499..01d10ed 100644
--- a/cipher/pubkey-util.c
+++ b/cipher/pubkey-util.c
@@ -850,6 +850,21 @@ _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
                                                  ctx->hash_algo);
         }
     }
+  else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue
+	   && (ctx->op == PUBKEY_OP_SIGN || ctx->op == PUBKEY_OP_VERIFY))
+    {
+      const void * value;
+      size_t valuelen;
+
+      if (sexp_length (lvalue) != 2)
+        rc = GPG_ERR_INV_OBJ;
+      else if ( !(value=sexp_nth_data (lvalue, 1, &valuelen))
+                || !valuelen )
+        rc = GPG_ERR_INV_OBJ;
+      else
+        rc = _gcry_rsa_pkcs1_encode_simple_for_sig (ret_mpi, ctx->nbits,
+                                                 value, valuelen);
+    }
   else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue
 	   && ctx->op == PUBKEY_OP_ENCRYPT)
     {
diff --git a/cipher/rsa-common.c b/cipher/rsa-common.c
index 4f5a659..0c8df3a 100644
--- a/cipher/rsa-common.c
+++ b/cipher/rsa-common.c
@@ -319,6 +319,71 @@ _gcry_rsa_pkcs1_encode_for_sig (gcry_mpi_t *r_result, unsigned int nbits,
   return rc;
 }
 
+/* Encode {VALUE,VALUELEN} for an NBITS keys using the pkcs#1 block
+   type 1 padding.  On success the result is stored as a new MPI at
+   R_RESULT.  On error the value at R_RESULT is undefined.
+
+   We encode the value in this way:
+
+     0  1  PAD(n bytes)  0  VALUE(valuelen bytes)
+
+   0   is a marker we unfortunately can't encode because we return an
+       MPI which strips all leading zeroes.
+   1   is the block type.
+   PAD consists of 0xff bytes.
+   0   marks the end of the padding.
+
+   (Note that PGP prior to version 2.3 encoded the message digest as:
+      0   1   MD(16 bytes)   0   PAD(n bytes)   1
+    The MD is always 16 bytes here because it's always MD5.  GnuPG
+    does not not support pre-v2.3 signatures, but I'm including this
+    comment so the information is easily found if needed.)
+*/
+gpg_err_code_t
+_gcry_rsa_pkcs1_encode_simple_for_sig (gcry_mpi_t *r_result, unsigned int nbits,
+                                const unsigned char *value, size_t valuelen)
+{
+  gcry_err_code_t rc = 0;
+  gcry_error_t err;
+  byte *frame = NULL;
+  size_t nframe = (nbits+7) / 8;
+  int i;
+  size_t n;
+
+  if ( !valuelen || valuelen + 4 > nframe)
+    {
+      /* Can't encode an DLEN byte digest MD into an NFRAME byte
+         frame.  */
+      return GPG_ERR_TOO_SHORT;
+    }
+
+  if ( !(frame = xtrymalloc (nframe)) )
+    return gpg_err_code_from_syserror ();
+
+  /* Assemble the pkcs#1 block type 1. */
+  n = 0;
+  frame[n++] = 0;
+  frame[n++] = 1; /* block type */
+  i = nframe - valuelen - 3 ;
+  gcry_assert (i > 1);
+  memset (frame+n, 0xff, i );
+  n += i;
+  frame[n++] = 0;
+  memcpy (frame+n, value, valuelen );
+  n += valuelen;
+  gcry_assert (n == nframe);
+
+  /* Convert it into an MPI. */
+  err = _gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, n, &nframe);
+  if (err)
+    rc = gcry_err_code (err);
+  else if (DBG_CIPHER)
+    log_mpidump ("PKCS#1 block type 1 encoded data", *r_result);
+  xfree (frame);
+
+  return rc;
+}
+
 
 /* Mask generation function for OAEP.  See RFC-3447 B.2.1.  */
 static gcry_err_code_t
diff --git a/tests/basic.c b/tests/basic.c
index 84a2f60..719fb0e 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -5361,7 +5361,7 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
       {	"(data\n (flags pkcs1)\n"
 	" (value #11223344556677889900AA#))\n",
 	GCRY_PK_RSA,
-	GPG_ERR_CONFLICT },
+	0 },
       { "(data\n (flags raw foo)\n"
 	" (value #11223344556677889900AA#))\n",
 	0,
-- 
1.8.5.1




More information about the Gcrypt-devel mailing list