[TESTING] Curve25519 encryption support

NIIBE Yutaka gniibe at fsij.org
Fri Jul 3 11:36:46 CEST 2015


Hello,

Here is a patch I am testing for Curve25519 encryption support (which
requires libgcrypt change for Curve25519).

I tested (1) key generation, (2) encryption, and (3) decryption.

There are things we need to decide: OID, algo ID, and format.
Currently, I use:

     OID: 1.3.6.1.4.1.3029.1.5.1
     algo ID: ECC = 18 (not 22 nor 23)
     Public key OpenPGP format: prefix 0x40 and native little endian
     Libgcrypt SEXP format: Use (flags eddsa) to specify
                             prefix 0x40 and native little endian

It works for me, but the code is not mature enough.

diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c
index 7a75801..92622ce 100644
--- a/common/openpgp-oid.c
+++ b/common/openpgp-oid.c
@@ -45,6 +45,7 @@ static struct {
   const char *alias;  /* NULL or alternative name of the curve.  */
 } oidtable[] = {

+  { "Curve25519",      "1.3.6.1.4.1.3029.1.5.1", 255, "curve25519" },
   { "Ed25519",         "1.3.6.1.4.1.11591.15.1", 255, "ed25519" },

   { "NIST P-256",      "1.2.840.10045.3.1.7",    256, "nistp256" },
@@ -65,6 +66,10 @@ static struct {
 static const char oid_ed25519[] =
   { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 };

+/* The OID for Curve25519 in OpenPGP format.  */
+static const char oid_curve25519[] =
+  { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 };
+

 /* Helper for openpgp_oid_from_str.  */
 static size_t
@@ -291,6 +296,22 @@ openpgp_oid_is_ed25519 (gcry_mpi_t a)
 }


+int
+openpgp_oid_is_curve25519 (gcry_mpi_t a)
+{
+  const unsigned char *buf;
+  unsigned int nbits;
+  size_t n;
+
+  if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
+    return 0;
+
+  buf = gcry_mpi_get_opaque (a, &nbits);
+  n = (nbits+7)/8;
+  return (n == DIM (oid_curve25519)
+          && !memcmp (buf, oid_curve25519, DIM (oid_curve25519)));
+}
+

 /* Map the Libgcrypt ECC curve NAME to an OID.  If R_NBITS is not NULL
    store the bit size of the curve there.  Returns NULL for unknown
diff --git a/common/util.h b/common/util.h
index b92d78c..2851cfd 100644
--- a/common/util.h
+++ b/common/util.h
@@ -318,6 +318,7 @@ size_t percent_unescape_inplace (char *string, int nulrepl);
 gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi);
 char *openpgp_oid_to_str (gcry_mpi_t a);
 int openpgp_oid_is_ed25519 (gcry_mpi_t a);
+int openpgp_oid_is_curve25519 (gcry_mpi_t a);
 const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits);
 const char *openpgp_oid_to_curve (const char *oid);
 const char *openpgp_enum_curves (int *idxp);
diff --git a/g10/ecdh.c b/g10/ecdh.c
index 9576a1c..a1b7ecf 100644
--- a/g10/ecdh.c
+++ b/g10/ecdh.c
@@ -134,9 +134,12 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi,
       }

     secret_x_size = (nbits+7)/8;
-    assert (nbytes > secret_x_size);
-    memmove (secret_x, secret_x+1, secret_x_size);
-    memset (secret_x+secret_x_size, 0, nbytes-secret_x_size);
+    assert (nbytes >= secret_x_size);
+    if ((nbytes & 1))
+      /* Remove the "04" prefix of non-compressed format.  */
+      memmove (secret_x, secret_x+1, secret_x_size);
+    if (nbytes - secret_x_size)
+      memset (secret_x+secret_x_size, 0, nbytes-secret_x_size);

     if (DBG_CRYPTO)
       log_printhex ("ECDH shared secret X is:", secret_x, secret_x_size );
diff --git a/g10/keygen.c b/g10/keygen.c
index 796d18f..3eb8e73 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -1520,6 +1520,13 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root,
        (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
          && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
         " transient-key" : ""));
+  else if (algo == PUBKEY_ALGO_ECDH && !strcmp (curve, "Curve25519"))
+    keyparms = xtryasprintf
+      ("(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))",
+       strlen (curve), curve,
+       (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY)
+         && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))?
+        " transient-key" : ""));
   else
     keyparms = xtryasprintf
       ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))",
@@ -2125,7 +2132,7 @@ ask_keysize (int algo, unsigned int primary_keysize)
    function may adjust.  Returns a malloced string with the name of
    the curve.  BOTH tells that gpg creates a primary and subkey. */
 static char *
-ask_curve (int *algo, int both)
+ask_curve (int *algo, int *subkey_algo)
 {
   struct {
     const char *name;
@@ -2176,7 +2183,7 @@ ask_curve (int *algo, int both)
         continue;
       if (!gcry_pk_get_curve (keyparms, 0, NULL))
         continue;
-      if (both && curves[idx].fix_curve)
+      if (subkey_algo && curves[idx].fix_curve)
         {
           /* Both Curve 25519 keys are to be created.  Check that
              Libgcrypt also supports the real Curve25519.  */
@@ -2241,6 +2248,11 @@ ask_curve (int *algo, int both)
           if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA)
               && curves[idx].fix_curve)
             {
+              if (subkey_algo && *subkey_algo == PUBKEY_ALGO_ECDSA)
+                {
+                  *subkey_algo = PUBKEY_ALGO_EDDSA;
+                  result = xstrdup ("Ed25519");
+                }
               *algo = PUBKEY_ALGO_EDDSA;
               result = xstrdup ("Ed25519");
             }
@@ -3672,7 +3684,7 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
               || algo == PUBKEY_ALGO_EDDSA
               || algo == PUBKEY_ALGO_ECDH)
             {
-              curve = ask_curve (&algo, both);
+              curve = ask_curve (&algo, &subkey_algo);
               r = xmalloc_clear( sizeof *r + 20 );
               r->key = pKEYTYPE;
               sprintf( r->u.value, "%d", algo);
@@ -3743,7 +3755,7 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
               || algo == PUBKEY_ALGO_EDDSA
               || algo == PUBKEY_ALGO_ECDH)
             {
-              curve = ask_curve (&algo, 0);
+              curve = ask_curve (&algo, NULL);
               nbits = 0;
               r = xmalloc_clear (sizeof *r + strlen (curve));
               r->key = pKEYCURVE;
@@ -4292,7 +4304,7 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
   else if (algo == PUBKEY_ALGO_ECDSA
            || algo == PUBKEY_ALGO_EDDSA
            || algo == PUBKEY_ALGO_ECDH)
-    curve = ask_curve (&algo, 0);
+    curve = ask_curve (&algo, NULL);
   else
     nbits = ask_keysize (algo, 0);

diff --git a/g10/keyid.c b/g10/keyid.c
index 90d982e..cfdf29d 100644
--- a/g10/keyid.c
+++ b/g10/keyid.c
@@ -761,12 +761,20 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array)
     case PUBKEY_ALGO_ECDH:
       {
         char *curve = openpgp_oid_to_str (pk->pkey[0]);
+
         if (!curve)
           err = gpg_error_from_syserror ();
         else
           {
+            int with_eddsa_flag = 0;
+
+            if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA
+                || (pk->pubkey_algo == PUBKEY_ALGO_ECDH
+                    && openpgp_oid_is_curve25519 (pk->pkey[0])))
+              with_eddsa_flag = 1;
+
             err = gcry_sexp_build (&s_pkey, NULL,
-                                   pk->pubkey_algo == PUBKEY_ALGO_EDDSA ?
+                                   with_eddsa_flag ?
                                    "(public-key(ecc(curve%s)(flags eddsa)(q%m)))"
                                    : "(public-key(ecc(curve%s)(q%m)))",
                                    curve, pk->pkey[1]);
diff --git a/g10/misc.c b/g10/misc.c
index 9134b28..8f2d379 100644
--- a/g10/misc.c
+++ b/g10/misc.c
@@ -612,11 +612,11 @@ openpgp_pk_algo_usage ( int algo )
           use = PUBKEY_USAGE_ENC;
           break;
       case PUBKEY_ALGO_DSA:
+      case PUBKEY_ALGO_ECDSA:
           use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
           break;
-      case PUBKEY_ALGO_ECDSA:
       case PUBKEY_ALGO_EDDSA:
-          use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
+          use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH;
       default:
           break;
     }
diff --git a/g10/pkglue.c b/g10/pkglue.c
index d72275b..e543ceb 100644
--- a/g10/pkglue.c
+++ b/g10/pkglue.c
@@ -228,9 +228,13 @@ pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data,
             rc = gpg_error_from_syserror ();
           else
             {
+              int with_eddsa_flag = openpgp_oid_is_curve25519 (pkey[0]);
+
               /* Now use the ephemeral secret to compute the shared point.  */
               rc = gcry_sexp_build (&s_pkey, NULL,
-                                    "(public-key(ecdh(curve%s)(q%m)))",
+                                    with_eddsa_flag ?
+                                    "(public-key(ecdh(curve%s)(flags eddsa)(q%m)))"
+                                    : "(public-key(ecdh(curve%s)(q%m)))",
                                     curve, pkey[1]);
               xfree (curve);
               /* Put K into a simplified S-expression.  */
diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c
index cb834af..fd7f812 100644
--- a/g10/pubkey-enc.c
+++ b/g10/pubkey-enc.c
@@ -250,8 +250,8 @@ get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
       if(err)
         goto leave;

-      /* Reuse NFRAME, which size is sufficient to include the session key.  */
-      err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, decoded);
+      xfree (frame);
+      err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded);
       mpi_release (decoded);
       if (err)
         goto leave;
--



More information about the Gnupg-devel mailing list