gcrypt: Optionally use internal OAEP padding.
authorAndre Noll <maan@systemlinux.org>
Mon, 4 Jul 2011 23:04:34 +0000 (01:04 +0200)
committerAndre Noll <maan@systemlinux.org>
Wed, 6 Jul 2011 06:47:34 +0000 (08:47 +0200)
libgcrypt supports OAEP padding since version 1.5.0, which has just
been released. Since want the paraslash gcrypt code to work also
for older gcrypt libraries, we check the library version at runtime
and fall back to the internal OAEP padding code if an old library
was detected.

This patch moves much of the rather ugly OAEP padding code into new
new helper decode_rsa() which reduces to a mere memcpy for newer
gcrypt versions. Much of the old code can be removed once all major
distributions ship libgcrypt-1.5.0 or later.

gcrypt.c

index 49e27f2..775abd7 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -19,6 +19,9 @@
 
 //#define GCRYPT_DEBUG 1
 
+static bool libgcrypt_has_oaep;
+static const char *rsa_decrypt_sexp;
+
 #ifdef GCRYPT_DEBUG
 static void dump_buffer(const char *msg, unsigned char *buf, int len)
 {
@@ -56,14 +59,32 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
 }
 
 /*
- * This is called at the beginning of every program that uses libgcrypt.  We
- * don't have to initialize any random seed here, but the call to gcry_control
- * is necessary to avoid warnings of the form "missing initialization - please
- * fix the application".
+ * This is called at the beginning of every program that uses libgcrypt. We
+ * don't have to initialize any random seed here, but we must initialize the
+ * gcrypt library. This task is performed by gcry_check_version() which can
+ * also check that the gcrypt library version is at least the minimal required
+ * version. This function also tells us whether we have to use our own OAEP
+ * padding code.
  */
 void init_random_seed_or_die(void)
 {
-       gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+       const char *ver, *req_ver;
+
+       ver = gcry_check_version(NULL);
+       req_ver = "1.4.0";
+       if (!gcry_check_version(req_ver)) {
+               PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n",
+                       req_ver, ver);
+               exit(EXIT_FAILURE);
+       }
+       req_ver = "1.5.0";
+       if (gcry_check_version(req_ver)) {
+               libgcrypt_has_oaep = true;
+               rsa_decrypt_sexp = "(enc-val(flags oaep)(rsa(a %m)))";
+       } else {
+               libgcrypt_has_oaep = false;
+               rsa_decrypt_sexp = "(enc-val(rsa(a %m)))";
+       }
 }
 
 /** S-expression for the public part of an RSA key. */
@@ -693,16 +714,74 @@ void free_asymmetric_key(struct asymmetric_key *key)
        free(key);
 }
 
+static int decode_rsa(gcry_sexp_t sexp, int key_size, unsigned char *outbuf,
+               size_t *nbytes)
+{
+       int ret;
+       gcry_error_t gret;
+       unsigned char oaep_buf[512];
+       gcry_mpi_t out_mpi;
+
+       if (libgcrypt_has_oaep) {
+               const char *p = gcry_sexp_nth_data(sexp, 1, nbytes);
+
+               if (!p) {
+                       PARA_ERROR_LOG("could not get data from list\n");
+                       return -E_OEAP;
+               }
+               memcpy(outbuf, p, *nbytes);
+               return 1;
+       }
+       out_mpi = gcry_sexp_nth_mpi(sexp, 0, GCRYMPI_FMT_USG);
+       if (!out_mpi)
+               return -E_SEXP_FIND;
+       gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf),
+               nbytes, out_mpi);
+       if (gret) {
+               PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret));
+               ret = -E_MPI_PRINT;
+               goto out_mpi_release;
+       }
+       /*
+        * An oaep-encoded buffer always starts with at least one zero byte.
+        * However, leading zeroes in an mpi are omitted in the output of
+        * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The
+        * alternative, GCRYMPI_FMT_STD, does not work either because here the
+        * leading zero(es) might also be omitted, depending on the value of
+        * the second byte.
+        *
+        * To circumvent this, we shift the oaep buffer to the right. But first
+        * we check that the buffer actually started with a zero byte, i.e. that
+        * nbytes < key_size. Otherwise a decoding error occurred.
+        */
+       ret = -E_SEXP_DECRYPT;
+       if (*nbytes >= key_size)
+               goto out_mpi_release;
+       memmove(oaep_buf + key_size - *nbytes, oaep_buf, *nbytes);
+       memset(oaep_buf, 0, key_size - *nbytes);
+
+       PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n",
+               key_size);
+       dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);;
+       unpad_oaep(oaep_buf, key_size, outbuf, nbytes);
+       PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n",
+               *nbytes);
+       dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);;
+       ret = 1;
+out_mpi_release:
+       gcry_mpi_release(out_mpi);
+       return ret;
+}
+
 int priv_decrypt(const char *key_file, unsigned char *outbuf,
                unsigned char *inbuf, int inlen)
 {
        gcry_error_t gret;
        int ret, key_size;
        struct asymmetric_key *priv;
-       gcry_mpi_t in_mpi = NULL, out_mpi = NULL;
+       gcry_mpi_t in_mpi = NULL;
        gcry_sexp_t in, out, priv_key;
        size_t nbytes;
-       unsigned char oaep_buf[512];
 
        PARA_INFO_LOG("decrypting %d byte input\n", inlen);
        /* key_file -> asymmetric key priv */
@@ -725,9 +804,8 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
                ret = -E_MPI_SCAN;
                goto key_release;
        }
-
        /* in_mpi -> in sexp */
-       gret = gcry_sexp_build(&in, NULL, "(enc-val(rsa(a %m)))", in_mpi);
+       gret = gcry_sexp_build(&in, NULL, rsa_decrypt_sexp, in_mpi);
        if (gret) {
                PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
                ret = -E_SEXP_BUILD;
@@ -741,47 +819,11 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
                ret = -E_SEXP_DECRYPT;
                goto in_release;
        }
-       out_mpi = gcry_sexp_nth_mpi(out, 0, GCRYMPI_FMT_USG);
-       if (!out_mpi) {
-               ret = -E_SEXP_FIND;
+       ret = decode_rsa(out, key_size, outbuf, &nbytes);
+       if (ret < 0)
                goto out_release;
-       }
-       gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf),
-               &nbytes, out_mpi);
-       if (gret) {
-               PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret));
-               ret = -E_MPI_PRINT;
-               goto out_mpi_release;
-       }
-       /*
-        * An oaep-encoded buffer always starts with at least one zero byte.
-        * However, leading zeroes in an mpi are omitted in the output of
-        * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The
-        * alternative, GCRYMPI_FMT_STD, does not work either because here the
-        * leading zero(es) might also be omitted, depending on the value of
-        * the second byte.
-        *
-        * To circumvent this, we shift the oaep buffer to the right. But first
-        * we check that the buffer actually started with a zero byte, i.e. that
-        * nbytes < key_size. Otherwise a decoding error occurred.
-        */
-       ret = -E_SEXP_DECRYPT;
-       if (nbytes >= key_size)
-               goto out_mpi_release;
-       memmove(oaep_buf + key_size - nbytes, oaep_buf, nbytes);
-       memset(oaep_buf, 0, key_size - nbytes);
-
-       PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n",
-               key_size);
-       dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);;
-       unpad_oaep(oaep_buf, key_size, outbuf, &nbytes);
-       PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n",
-               nbytes);
-       dump_buffer("unpadded decrypted buffer", outbuf, nbytes);;
+       PARA_INFO_LOG("successfully decrypted %zu byte message\n", nbytes);
        ret = nbytes;
-       PARA_INFO_LOG("successfully decrypted %u byte message\n", ret);
-out_mpi_release:
-       gcry_mpi_release(out_mpi);
 out_release:
        gcry_sexp_release(out);
 in_release:
@@ -799,12 +841,10 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
                unsigned len, unsigned char *outbuf)
 {
        gcry_error_t gret;
-       const size_t pad_size = 256;
        gcry_sexp_t pub_key, in, out, out_a;
        gcry_mpi_t out_mpi = NULL;
        size_t nbytes;
        int ret;
-       unsigned char padded_input[256];
 
        PARA_INFO_LOG("encrypting %u byte input with %d-byte key\n", len, pub->num_bytes);
 
@@ -812,13 +852,18 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
        pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0);
        if (!pub_key)
                return -E_SEXP_FIND;
-
-       /* inbuf -> padded inbuf */
-       pad_oaep(inbuf, len, padded_input, pad_size);
-
-       /* padded inbuf -> in sexp */
-       gret = gcry_sexp_build(&in, NULL, "(data(flags raw)(value %b))",
-               pad_size, padded_input);
+       if (libgcrypt_has_oaep) {
+               gret = gcry_sexp_build(&in, NULL,
+                       "(data(flags oaep)(value %b))", len, inbuf);
+       } else {
+               unsigned char padded_input[256];
+               const size_t pad_size = 256;
+               /* inbuf -> padded inbuf */
+               pad_oaep(inbuf, len, padded_input, pad_size);
+               /* padded inbuf -> in sexp */
+               gret = gcry_sexp_build(&in, NULL,
+                       "(data(flags raw)(value %b))", pad_size, padded_input);
+       }
        if (gret) {
                PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
                ret = -E_SEXP_BUILD;