Prepare support for EdDSA.
authorWerner Koch <wk@gnupg.org>
Wed, 4 Sep 2013 09:20:57 +0000 (11:20 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 4 Sep 2013 09:25:06 +0000 (11:25 +0200)
* src/cipher.h (PUBKEY_FLAG_EDDSA): New.
* cipher/pubkey.c (pubkey_verify): Repalce args CMP and OPAQUEV by
CTX.  Pass flags and hash algo to the verify function.  Change all
verify functions to accept these args.
(sexp_data_to_mpi): Implement new flag "eddsa".
(gcry_pk_verify): Pass CTX instead of the compare function to
pubkey_verify.
* cipher/ecc.c (sign): Rename to sign_ecdsa.  Change all callers.
(verify): Rename to verify_ecdsa.  Change all callers.
(sign_eddsa, verify_eddsa): New stub functions.
(ecc_sign): Divert to sign_ecdsa or sign_eddsa.
(ecc_verify): Divert to verify_ecdsa or verify_eddsa.

cipher/dsa.c
cipher/ecc.c
cipher/elgamal.c
cipher/pubkey.c
cipher/rsa.c
doc/gcrypt.texi
src/cipher.h
src/gcrypt-module.h

index ac2dee1..3a64dda 100644 (file)
@@ -1004,7 +1004,8 @@ dsa_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey,
 
 static gcry_err_code_t
 dsa_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
-            int (*cmp) (void *, gcry_mpi_t), void *opaquev)
+            int (*cmp) (void *, gcry_mpi_t), void *opaquev,
+            int flags, int hashalgo)
 {
   gcry_err_code_t err = GPG_ERR_NO_ERROR;
   DSA_public_key pk;
@@ -1012,6 +1013,8 @@ dsa_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
   (void)algo;
   (void)cmp;
   (void)opaquev;
+  (void)flags;
+  (void)hashalgo;
 
   if ((! data[0]) || (! data[1]) || (! hash)
       || (! pkey[0]) || (! pkey[1]) || (! pkey[2]) || (! pkey[3]))
index 8a7ca0b..0cb279f 100644 (file)
@@ -77,12 +77,11 @@ static void *progress_cb_data;
 /* Local prototypes. */
 static void test_keys (ECC_secret_key * sk, unsigned int nbits);
 static int check_secret_key (ECC_secret_key * sk);
-static gpg_err_code_t sign (gcry_mpi_t input, ECC_secret_key *skey,
-                            gcry_mpi_t r, gcry_mpi_t s,
-                            int flags, int hashalgo);
-static gpg_err_code_t verify (gcry_mpi_t input, ECC_public_key *pkey,
-                              gcry_mpi_t r, gcry_mpi_t s);
-
+static gpg_err_code_t sign_ecdsa (gcry_mpi_t input, ECC_secret_key *skey,
+                                  gcry_mpi_t r, gcry_mpi_t s,
+                                  int flags, int hashalgo);
+static gpg_err_code_t verify_ecdsa (gcry_mpi_t input, ECC_public_key *pkey,
+                                    gcry_mpi_t r, gcry_mpi_t s);
 
 static gcry_mpi_t gen_y_2 (gcry_mpi_t x, elliptic_curve_t * base);
 
@@ -284,10 +283,10 @@ test_keys (ECC_secret_key *sk, unsigned int nbits)
 
   gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
 
-  if (sign (test, sk, r, s, 0, 0) )
+  if (sign_ecdsa (test, sk, r, s, 0, 0) )
     log_fatal ("ECDSA operation: sign failed\n");
 
-  if (verify (test, &pk, r, s))
+  if (verify_ecdsa (test, &pk, r, s))
     {
       log_fatal ("ECDSA operation: sign, verify failed\n");
     }
@@ -411,13 +410,13 @@ check_secret_key (ECC_secret_key * sk)
 }
 
 
-/*
+/* Compute an ECDSA signature.
  * Return the signature struct (r,s) from the message hash.  The caller
  * must have allocated R and S.
  */
 static gpg_err_code_t
-sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s,
-      int flags, int hashalgo)
+sign_ecdsa (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s,
+            int flags, int hashalgo)
 {
   gpg_err_code_t err = 0;
   int extraloops = 0;
@@ -532,11 +531,12 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s,
 }
 
 
-/*
+/* Verify an ECDSA signature.
  * Check if R and S verifies INPUT.
  */
 static gpg_err_code_t
-verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
+verify_ecdsa (gcry_mpi_t input, ECC_public_key *pkey,
+              gcry_mpi_t r, gcry_mpi_t s)
 {
   gpg_err_code_t err = 0;
   gcry_mpi_t h, h1, h2, x;
@@ -615,6 +615,59 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
 
 
 \f
+/* Compute an EdDSA signature. See:
+ *   [ed25519] 23pp. (PDF) Daniel J. Bernstein, Niels Duif, Tanja
+ *   Lange, Peter Schwabe, Bo-Yin Yang. High-speed high-security
+ *   signatures.  Journal of Cryptographic Engineering 2 (2012), 77-89.
+ *   Document ID: a1a62a2f76d23f65d622484ddd09caf8.
+ *   URL: http://cr.yp.to/papers.html#ed25519. Date: 2011.09.26.
+ *
+ * Despite that this function requires the specification of a hash
+ * algorithm, we only support what has been specified by the paper.
+ * This may change in the future.  Note that we don't check the used
+ * curve; the user is responsible to use Ed25519.
+ *
+ * Return the signature struct (r,s) from the message hash.  The caller
+ * must have allocated R and S.
+ */
+static gpg_err_code_t
+sign_eddsa (gcry_mpi_t input, ECC_secret_key *skey,
+            gcry_mpi_t r, gcry_mpi_t s, int hashalgo)
+{
+  (void)skey;
+  (void)r;
+  (void)s;
+
+  if (!mpi_is_opaque (input))
+    return GPG_ERR_INV_DATA;
+  if (hashalgo != GCRY_MD_SHA512)
+    return GPG_ERR_DIGEST_ALGO;
+
+  return GPG_ERR_NOT_IMPLEMENTED;
+}
+
+
+/* Verify an EdDSA signature.  See sign_eddsa for the reference.
+ * Check if R and S verifies INPUT.
+ */
+static gpg_err_code_t
+verify_eddsa (gcry_mpi_t input, ECC_public_key *pkey,
+              gcry_mpi_t r, gcry_mpi_t s, int hashalgo)
+{
+  (void)pkey;
+  (void)r;
+  (void)s;
+
+  if (!mpi_is_opaque (input))
+    return GPG_ERR_INV_DATA;
+  if (hashalgo != GCRY_MD_SHA512)
+    return GPG_ERR_DIGEST_ALGO;
+
+  return GPG_ERR_NOT_IMPLEMENTED;
+}
+
+
+\f
 /*********************************************
  **************  interface  ******************
  *********************************************/
@@ -796,7 +849,10 @@ ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey,
 
   resarr[0] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
   resarr[1] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
-  err = sign (data, &sk, resarr[0], resarr[1], flags, hashalgo);
+  if ((flags & PUBKEY_FLAG_EDDSA))
+    err = sign_eddsa (data, &sk, resarr[0], resarr[1], hashalgo);
+  else
+    err = sign_ecdsa (data, &sk, resarr[0], resarr[1], flags, hashalgo);
   if (err)
     {
       mpi_free (resarr[0]);
@@ -810,7 +866,8 @@ ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey,
 
 static gcry_err_code_t
 ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
-            int (*cmp)(void *, gcry_mpi_t), void *opaquev)
+            int (*cmp)(void *, gcry_mpi_t), void *opaquev,
+            int flags, int hashalgo)
 {
   gpg_err_code_t err;
   ECC_public_key pk;
@@ -843,7 +900,11 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
       return err;
     }
 
-  if (mpi_is_opaque (hash))
+  if ((flags & PUBKEY_FLAG_EDDSA))
+    {
+      err = verify_eddsa (hash, &pk, data[0], data[1], hashalgo);
+    }
+  else if (mpi_is_opaque (hash))
     {
       const void *abuf;
       unsigned int abits, qbits;
@@ -858,12 +919,12 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
           if (abits > qbits)
             gcry_mpi_rshift (a, a, abits - qbits);
 
-          err = verify (a, &pk, data[0], data[1]);
+          err = verify_ecdsa (a, &pk, data[0], data[1]);
           gcry_mpi_release (a);
         }
     }
   else
-    err = verify (hash, &pk, data[0], data[1]);
+    err = verify_ecdsa (hash, &pk, data[0], data[1]);
 
   point_free (&pk.E.G);
   point_free (&pk.Q);
index 7540e3f..d105cb4 100644 (file)
@@ -789,7 +789,8 @@ elg_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey,
 
 static gcry_err_code_t
 elg_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
-            int (*cmp) (void *, gcry_mpi_t), void *opaquev)
+            int (*cmp) (void *, gcry_mpi_t), void *opaquev,
+            int flags, int hashalgo)
 {
   gcry_err_code_t err = GPG_ERR_NO_ERROR;
   ELG_public_key pk;
@@ -797,6 +798,8 @@ elg_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
   (void)algo;
   (void)cmp;
   (void)opaquev;
+  (void)flags;
+  (void)hashalgo;
 
   if (mpi_is_opaque (hash))
     return GPG_ERR_INV_DATA;
index e6c1cf6..fb8de14 100644 (file)
@@ -41,8 +41,7 @@ static gcry_err_code_t pubkey_sign (int algo, gcry_mpi_t *resarr,
                                     struct pk_encoding_ctx *ctx);
 static gcry_err_code_t pubkey_verify (int algo, gcry_mpi_t hash,
                                       gcry_mpi_t *data, gcry_mpi_t *pkey,
-                                    int (*cmp) (void *, gcry_mpi_t),
-                                      void *opaque);
+                                      struct pk_encoding_ctx *ctx);
 
 
 /* A dummy extraspec so that we do not need to tests the extraspec
@@ -179,7 +178,8 @@ dummy_sign (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data,
 static gcry_err_code_t
 dummy_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data,
               gcry_mpi_t *pkey,
-             int (*cmp) (void *, gcry_mpi_t), void *opaquev)
+             int (*cmp) (void *, gcry_mpi_t), void *opaquev,
+              int flags, int hashalgo)
 {
   (void)algorithm;
   (void)hash;
@@ -187,6 +187,8 @@ dummy_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data,
   (void)pkey;
   (void)cmp;
   (void)opaquev;
+  (void)flags;
+  (void)hashalgo;
   fips_signal_error ("using dummy public key function");
   return GPG_ERR_NOT_IMPLEMENTED;
 }
@@ -757,8 +759,7 @@ pubkey_sign (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data,
  */
 static gcry_err_code_t
 pubkey_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data,
-               gcry_mpi_t *pkey,
-              int (*cmp)(void *, gcry_mpi_t), void *opaquev)
+               gcry_mpi_t *pkey, struct pk_encoding_ctx *ctx)
 {
   gcry_pk_spec_t *pubkey;
   gcry_module_t module;
@@ -780,7 +781,9 @@ pubkey_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data,
   if (module)
     {
       pubkey = (gcry_pk_spec_t *) module->spec;
-      rc = pubkey->verify (algorithm, hash, data, pkey, cmp, opaquev);
+      rc = pubkey->verify (algorithm, hash, data, pkey,
+                           ctx->verify_cmp, ctx,
+                           ctx->flags, ctx->hash_algo);
       _gcry_module_release (module);
       goto ready;
     }
@@ -2484,7 +2487,7 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo,
    (<mpi>)
    or
    (data
-    [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979])]
+    [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979, eddsa])]
     [(hash <algo> <value>)]
     [(value <text>)]
     [(hash-algo <algo>)]
@@ -2496,7 +2499,9 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_module_t *retalgo,
    Either the VALUE or the HASH element must be present for use
    with signatures.  VALUE is used for encryption.
 
-   HASH-ALGO and LABEL are specific to OAEP.
+   HASH-ALGO is specific to OAEP and EDDSA.
+
+   LABEL is specific to OAEP.
 
    SALT-LENGTH is for PSS.
 
@@ -2533,8 +2538,13 @@ sexp_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
             s = gcry_sexp_nth_data (lflags, i, &n);
             if (!s)
               ; /* not a data element*/
-           else if (n == 7 && ! memcmp (s, "rfc6979", 7))
+           else if (n == 7 && !memcmp (s, "rfc6979", 7))
              parsed_flags |= PUBKEY_FLAG_RFC6979;
+           else if (n == 5 && !memcmp (s, "eddsa", 5))
+              {
+                ctx->encoding = PUBKEY_ENC_RAW;
+                parsed_flags |= PUBKEY_FLAG_EDDSA;
+              }
             else if ( n == 3 && !memcmp (s, "raw", 3)
                       && ctx->encoding == PUBKEY_ENC_UNKNOWN)
               {
@@ -2570,6 +2580,54 @@ sexp_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
     rc = GPG_ERR_INV_OBJ; /* none or both given */
   else if (unknown_flag)
     rc = GPG_ERR_INV_FLAG;
+  else if (ctx->encoding == PUBKEY_ENC_RAW
+           && (parsed_flags & PUBKEY_FLAG_EDDSA))
+    {
+      /* Prepare for EdDSA.  */
+      gcry_sexp_t list;
+      void *value;
+      size_t valuelen;
+
+      if (lvalue)
+        {
+          rc = GPG_ERR_INV_OBJ;
+          goto leave;
+        }
+      /* Get HASH-ALGO. */
+      list = gcry_sexp_find_token (ldata, "hash-algo", 0);
+      if (list)
+        {
+          s = gcry_sexp_nth_data (list, 1, &n);
+          if (!s)
+            rc = GPG_ERR_NO_OBJ;
+          else
+            {
+              ctx->hash_algo = get_hash_algo (s, n);
+              if (!ctx->hash_algo)
+                rc = GPG_ERR_DIGEST_ALGO;
+            }
+          gcry_sexp_release (list);
+        }
+      else
+        rc = GPG_ERR_INV_OBJ;
+      if (rc)
+        goto leave;
+
+      /* Get VALUE.  */
+      value = gcry_sexp_nth_buffer (lvalue, 1, &valuelen);
+      if (!value)
+        rc = GPG_ERR_INV_OBJ;
+      else if ((valuelen * 8) < valuelen)
+        {
+          gcry_free (value);
+          rc = GPG_ERR_TOO_LARGE;
+        }
+      if (rc)
+        goto leave;
+
+      /* Note that mpi_set_opaque takes ownership of VALUE.  */
+      *ret_mpi = gcry_mpi_set_opaque (NULL, value, valuelen*8);
+    }
   else if (ctx->encoding == PUBKEY_ENC_RAW && lhash
            && (explicit_raw || (parsed_flags & PUBKEY_FLAG_RFC6979)))
     {
@@ -3406,8 +3464,7 @@ gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey)
   if (rc)
     goto leave;
 
-  rc = pubkey_verify (module_key->mod_id, hash, sig, pkey,
-                     ctx.verify_cmp, &ctx);
+  rc = pubkey_verify (module_key->mod_id, hash, sig, pkey, &ctx);
 
  leave:
   if (pkey)
index c9fcebf..bb22d05 100644 (file)
@@ -1020,8 +1020,8 @@ rsa_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey,
 
 static gcry_err_code_t
 rsa_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
-                 int (*cmp) (void *opaque, gcry_mpi_t tmp),
-                 void *opaquev)
+            int (*cmp) (void *opaque, gcry_mpi_t tmp), void *opaquev,
+            int flags, int hashalgo)
 {
   RSA_public_key pk;
   gcry_mpi_t result;
@@ -1030,6 +1030,8 @@ rsa_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
   (void)algo;
   (void)cmp;
   (void)opaquev;
+  (void)flags;
+  (void)hashalgo;
 
   if (mpi_is_opaque (hash))
     return GPG_ERR_INV_DATA;
index 770a245..4c8cdb1 100644 (file)
@@ -2364,6 +2364,7 @@ Here, the data to be signed is directly given as an @var{MPI}.
 
 @noindent
 For DSA the input data is expected in this format:
+
 @example
 (data
   (flags raw)
@@ -2411,11 +2412,28 @@ S-expression returned is:
 @end example
 
 Where @var{r-mpi} and @var{s-mpi} are the result of the DSA sign
-operation.  For Elgamal signing (which is slow, yields large numbers
-and probably is not as secure as the other algorithms), the same format is
-used with "elg" replacing "dsa"; for ECDSA signing, the same format is used
+operation.
+
+For Elgamal signing (which is slow, yields large numbers and probably
+is not as secure as the other algorithms), the same format is used
+with "elg" replacing "dsa"; for ECDSA signing, the same format is used
 with "ecdsa" replacing "dsa".
 
+For the EdDSA algorithm (cf. Ed25515) the required input parameters are:
+
+@example
+(data
+  (flags eddsa)
+  (hash-algo sha-512)
+  (value @var{message}))
+@end example
+
+Note that the @var{message} may be of any length; hashing is part of
+the algorithm.  Using a large data block for @var{message} is not
+suggested; in that case the used protocol should better require that a
+hash of the message is used as input to the EdDSA algorithm.
+
+
 @end deftypefun
 @c end gcry_pk_sign
 
index bb92758..3674c2d 100644 (file)
@@ -28,6 +28,7 @@
 
 #define PUBKEY_FLAG_NO_BLINDING    (1 << 0)
 #define PUBKEY_FLAG_RFC6979        (1 << 1)
+#define PUBKEY_FLAG_EDDSA          (1 << 2)
 
 enum pk_operation
   {
index 5276e8f..75ca8ab 100644 (file)
@@ -137,7 +137,9 @@ typedef gcry_err_code_t (*gcry_pk_verify_t) (int algo,
                                             gcry_mpi_t *data,
                                             gcry_mpi_t *pkey,
                                             int (*cmp) (void *, gcry_mpi_t),
-                                            void *opaquev);
+                                            void *opaquev,
+                                             int flags,
+                                             int hashalgo);
 
 /* Type for the pk_get_nbits function.  */
 typedef unsigned (*gcry_pk_get_nbits_t) (int algo, gcry_mpi_t *pkey);