Smartcard EdDSA Support

NIIBE Yutaka gniibe at fsij.org
Thu Apr 3 10:03:06 CEST 2014


Hello,

Thank you for your comment.  I've integrated the EdDSA code into Gnuk.
Now, I can login using OpenSSH with Gnuk Token by EdDSA.

Here is a patch to master branch of GnuPG, to show it's not that
large.

I need to clean up this before commit.  I know there is still at least
a bug for MPI/opaque-bytes.


-- 
diff --git a/agent/pksign.c b/agent/pksign.c
index fb593a6..94d1584 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -324,12 +324,18 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
       int key_type;
       int is_RSA = 0;
       int is_ECDSA = 0;
+      int is_EdDSA = 0;
 
-      key_type = agent_is_dsa_key (s_skey);
-      if (key_type == 0)
-        is_RSA = 1;
-      else if (key_type == GCRY_PK_ECDSA)
-        is_ECDSA = 1;
+      if (agent_is_eddsa_key (s_skey))
+        is_EdDSA = 1;
+      else
+        {
+          key_type = agent_is_dsa_key (s_skey);
+          if (key_type == 0)
+            is_RSA = 1;
+          else if (key_type == GCRY_PK_ECDSA)
+            is_ECDSA = 1;
+        }
 
       rc = divert_pksign (ctrl,
                           data, datalen,
@@ -356,6 +362,24 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
 
           rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%b)))", len, buf);
         }
+      else if (is_EdDSA)
+        {
+          unsigned char *r_buf_allocated = NULL;
+          unsigned char *s_buf_allocated = NULL;
+          unsigned char *r_buf, *s_buf;
+          int r_buflen, s_buflen;
+
+          r_buflen = s_buflen = len/2;
+
+          r_buf = buf;
+          s_buf = buf + len/2;
+
+          rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(eddsa(r%b)(s%b)))",
+                                r_buflen, r_buf,
+                                s_buflen, s_buf);
+          xfree (r_buf_allocated);
+          xfree (s_buf_allocated);
+        }
       else if (is_ECDSA)
         {
           unsigned char *r_buf_allocated = NULL;
diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c
index a2a3617..94a2296 100644
--- a/common/openpgp-oid.c
+++ b/common/openpgp-oid.c
@@ -39,7 +39,7 @@
 
 /* The OID for Curve Ed25519 in OpenPGP format.  */
 static const char oid_ed25519[] =
-  { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 };
+  { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 };
 

 /* Helper for openpgp_oid_from_str.  */
diff --git a/g10/keygen.c b/g10/keygen.c
index 135699d..f3052e4 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -4031,7 +4031,11 @@ 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 ();
+    {
+      curve = ask_curve ();
+      if (curve && !strcmp (curve, "Ed25519"))
+        algo = PUBKEY_ALGO_EDDSA;
+    }
   else
     nbits = ask_keysize (algo, 0);
 
diff --git a/g10/keyid.c b/g10/keyid.c
index 9ed64a4..4ce77aa 100644
--- a/g10/keyid.c
+++ b/g10/keyid.c
@@ -813,7 +813,9 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array)
         else
           {
             err = gcry_sexp_build (&s_pkey, NULL,
-                                   "(public-key(ecc(curve%s)(q%m)))",
+                                   pk->pubkey_algo == PUBKEY_ALGO_EDDSA ?
+                                   "(public-key(ecc(curve%s)(flags eddsa)(q%m)))"
+                                   : "(public-key(ecc(curve%s)(q%m)))", 
                                    curve, pk->pkey[1]);
             xfree (curve);
           }
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index da1bec9..4c14e91 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -122,6 +122,7 @@ typedef enum
   {
     KEY_TYPE_ECDH,
     KEY_TYPE_ECDSA,
+    KEY_TYPE_EDDSA,
     KEY_TYPE_RSA,
   }
 key_type_t;
@@ -146,7 +147,8 @@ enum
     CURVE_NIST_P384,
     CURVE_NIST_P521,
     CURVE_SEC_P256K1,
-    CURVE_UNKOWN,
+    CURVE_ED25519,
+    CURVE_UNKNOWN,
   };
 

@@ -238,6 +240,10 @@ struct app_local_s {
       struct {
         int curve;
         int hashalgo;
+      } eddsa;
+      struct {
+        int curve;
+        int hashalgo;
         int cipheralgo;
       } ecdh;
     };
@@ -746,6 +752,8 @@ get_algo_byte (key_type_t key_type)
     return 19;
   else if (key_type == KEY_TYPE_ECDH)
     return 18;
+  else if (key_type == KEY_TYPE_EDDSA)
+    return 105;                 /* (experimental) */
   else
     return 1;  /* RSA */
 }
@@ -770,7 +778,8 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
   int i;
 
   n = 6;    /* key packet version, 4-byte timestamps, and algorithm */
-  if (key_type == KEY_TYPE_RSA || key_type == KEY_TYPE_ECDSA)
+  if (key_type == KEY_TYPE_RSA || key_type == KEY_TYPE_ECDSA
+      || key_type == KEY_TYPE_EDDSA)
     argc = 2;
   else if (key_type == KEY_TYPE_ECDH)
     argc = 3;
@@ -935,11 +944,21 @@ get_ecc_key_parameters (int curve, int *r_n_bits, const char **r_curve_oid)
       *r_n_bits = 521;
       *r_curve_oid = "1.3.132.0.35";
     }
-  else
+  else if (curve == CURVE_SEC_P256K1)
     {
       *r_n_bits = 256;
       *r_curve_oid = "1.3.132.0.10";
     }
+  else if (curve == CURVE_ED25519)
+    {
+      *r_n_bits = 256;
+      *r_curve_oid = "1.3.6.1.4.1.11591.15.1";
+    }
+  else
+    {
+      *r_n_bits = 0;
+      *r_curve_oid = "1.3.6.1.4.1.11591.2.12242973"; /* gnu.gnupg.badoid */
+    }
 }
 
 static void
@@ -973,6 +992,15 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
                 app->app_local->keyattr[number].ecdh.hashalgo,
                 app->app_local->keyattr[number].ecdh.cipheralgo);
     }
+  else if (app->app_local->keyattr[number].key_type == KEY_TYPE_EDDSA)
+    {
+      get_ecc_key_parameters (app->app_local->keyattr[number].eddsa.curve,
+                              &n_bits, &curve_oid);
+      snprintf (buffer, sizeof buffer, "%d 105 %u %d %s",
+                number+1, n_bits,
+                app->app_local->keyattr[number].ecdh.hashalgo,
+                curve_oid);
+    }
   else
     snprintf (buffer, sizeof buffer, "0 0 UNKNOWN");
 
@@ -1282,8 +1310,12 @@ get_curve_name (int curve)
     return "NIST P-384";
   else if (curve == CURVE_NIST_P521)
     return "NIST P-521";
-  else
+  else if (curve == CURVE_SEC_P256K1)
     return "secp256k1";
+  else if (curve == CURVE_ED25519)
+    return "Ed25519";
+  else
+    return "unknown";
 }
 

@@ -1455,7 +1487,8 @@ get_public_key (app_t app, int keyno)
       goto leave;
     }
   /* Prepend numbers with a 0 if needed.  */
-  if (mlen && (*m & 0x80))
+  if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_EDDSA
+      && mlen && (*m & 0x80))
     {
       *mbuf = 0;
       memcpy (mbuf+1, m, mlen);
@@ -1521,6 +1554,29 @@ get_public_key (app_t app, int keyno)
       gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
       gcry_sexp_release (s_pkey);
     }
+  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_EDDSA)
+    {
+      const char *curve_name
+        = get_curve_name (app->app_local->keyattr[keyno].eddsa.curve);
+
+      err = gcry_sexp_build (&s_pkey, NULL,
+                             "(public-key(ecc(curve%s)(flags eddsa)(q%b)))",
+                             curve_name, mlen, mbuf);
+      if (err)
+        goto leave;
+
+      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+
+      keybuf = xtrymalloc (len);
+      if (!keybuf)
+        {
+          gcry_sexp_release (s_pkey);
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+      gcry_sexp_release (s_pkey);
+    }
   else
     {
       err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
@@ -2695,9 +2751,9 @@ build_privkey_template (app_t app, int keyno,
 }
 
 static gpg_error_t
-build_ecdsa_privkey_template (app_t app, int keyno,
-                              const unsigned char *ecc_d, size_t ecc_d_len,
-                              unsigned char **result, size_t *resultlen)
+build_ecc_privkey_template (app_t app, int keyno,
+                            const unsigned char *ecc_d, size_t ecc_d_len,
+                            unsigned char **result, size_t *resultlen)
 {
   unsigned char privkey[2];
   size_t privkey_len;
@@ -3179,9 +3235,9 @@ ecdh_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
 

 static gpg_error_t
-ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
-                void *pincb_arg, int keyno,
-                const unsigned char *buf, size_t buflen, int depth)
+ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+              void *pincb_arg, int keyno,
+              const unsigned char *buf, size_t buflen, int depth)
 {
   gpg_error_t err;
   const unsigned char *tok;
@@ -3194,10 +3250,15 @@ ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   size_t template_len;
   unsigned char fprbuf[20];
   u32 created_at = 0;
-  int curve = CURVE_UNKOWN;
-
-  /* (private-key(ecdsa(curve%s)(q%m)(d%m))): curve = "1.2.840.10045.3.1.7" */
-  /* (private-key(ecc(curve%s)(q%m)(d%m))): curve = "secp256k1" */
+  int curve = CURVE_UNKNOWN;
+
+  /* (private-key(ecdsa(curve%s)(q%m)(d%m))(created-at%d)):
+     curve = "1.2.840.10045.3.1.7" */
+  /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
+     curve = "secp256k1" */
+  /* (private-key(ecc(curve%s)(flags eddsa)(q%b)(d%b))(created-at%d)):
+      curve = "Ed25519" */
+  log_printhex ("q: ", buf, buflen);
   last_depth1 = depth;
   while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
          && depth && depth >= last_depth1)
@@ -3219,33 +3280,34 @@ ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
             curve = CURVE_NIST_P256;
           else if (tok && toklen == 9 && !memcmp (tok, "secp256k1", 9))
             curve = CURVE_SEC_P256K1;
+          else if (tok && toklen == 7 && !memcmp (tok, "Ed25519", 7))
+            curve = CURVE_ED25519;
         }
       else if (tok && toklen == 1)
         {
-          const unsigned char **mpi;
-          size_t *mpi_len;
+          const unsigned char **buf2;
+          size_t *buf2len;
 
           switch (*tok)
             {
-            case 'q': mpi = &ecc_q; mpi_len = &ecc_q_len; break;
-            case 'd': mpi = &ecc_d; mpi_len = &ecc_d_len; break;
-            default: mpi = NULL;  mpi_len = NULL; break;
+            case 'q': buf2 = &ecc_q; buf2len = &ecc_q_len; break;
+            case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; break;
+            default: buf2 = NULL;  buf2len = NULL; break;
             }
-          if (mpi && *mpi)
+          if (buf2 && *buf2)
             {
               err = gpg_error (GPG_ERR_DUP_VALUE);
               goto leave;
             }
           if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
             goto leave;
-          if (tok && mpi)
-            {
-              /* Strip off leading zero bytes and save. */
-              for (;toklen && !*tok; toklen--, tok++)
-                ;
-              *mpi = tok;
-              *mpi_len = toklen;
-            }
+          if (tok && buf2 && curve != CURVE_ED25519)
+            /* It's MPI.  Strip off leading zero bytes and save. */
+            for (;toklen && !*tok; toklen--, tok++)
+              ;
+
+          *buf2 = tok;
+          *buf2len = toklen;
         }
       /* Skip until end of list. */
       last_depth2 = depth;
@@ -3312,9 +3374,9 @@ ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
          the OpenPGP card specs version 2.0.  */
       int exmode;
 
-      err = build_ecdsa_privkey_template (app, keyno,
-                                          ecc_d, ecc_d_len,
-                                          &template, &template_len);
+      err = build_ecc_privkey_template (app, keyno,
+                                        ecc_d, ecc_d_len,
+                                        &template, &template_len);
       if (err)
         goto leave;
 
@@ -3343,11 +3405,14 @@ ecdsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
     }
 
   err = store_fpr (app, keyno, created_at, fprbuf, app->card_version,
-                   KEY_TYPE_ECDSA,
-                   curve == CURVE_NIST_P256?
+                   curve == CURVE_ED25519 ? KEY_TYPE_EDDSA : KEY_TYPE_ECDSA,
+                   curve == CURVE_ED25519 ?
+                   "\x09\x2b\x06\x01\x04\x01\xda\x47\x0f\x01"
+                   : curve == CURVE_NIST_P256 ?
                    "\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"
                    : "\05\x2b\x81\x04\x00\x0a",
-                   curve == CURVE_NIST_P256? 9 : 6,
+                   curve == CURVE_ED25519 ? 10
+                   : curve == CURVE_NIST_P256? 9 : 6,
                    ecc_q, ecc_q_len);
   if (err)
     goto leave;
@@ -3425,7 +3490,7 @@ do_writekey (app_t app, ctrl_t ctrl,
   else if ((tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0
             && (keyno == 0 || keyno == 2))
            || (tok && toklen == 5 && memcmp ("ecdsa", tok, toklen) == 0))
-    ecdsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
+    ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
   else if ((tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0
             && keyno == 1)
            || (tok && toklen == 4 && memcmp ("ecdh", tok, toklen) == 0))
@@ -3912,7 +3977,8 @@ do_auth (app_t app, const char *keyidstr,
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (indatalen > 101) /* For a 2048 bit key. */
+  if (app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
+      && indatalen > 101) /* For a 2048 bit key. */
     return gpg_error (GPG_ERR_INV_VALUE);
 
   if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECDSA
@@ -3922,6 +3988,12 @@ do_auth (app_t app, const char *keyidstr,
       indata = p;
       indatalen -= 19;
     }
+  else if (app->app_local->keyattr[2].key_type == KEY_TYPE_EDDSA)
+    {
+      const char *p = (const char *)indata + 15;
+      indata = p;
+      indatalen -= 15;
+    }
 
   /* Check whether an OpenPGP card of any version has been requested. */
   if (!strcmp (keyidstr, "OPENPGP.3"))
@@ -4298,14 +4370,18 @@ parse_ecc_curve (const unsigned char *buffer, size_t buflen)
 {
   int curve;
 
-  if (buflen == 6 && buffer[5] == 0x22)
+  if (buflen == 5 && buffer[5] == 0x22)
     curve = CURVE_NIST_P384;
-  else if (buflen == 6 && buffer[5] == 0x23)
+  else if (buflen == 5 && buffer[5] == 0x23)
     curve = CURVE_NIST_P521;
-  else if (buflen == 9)
+  else if (buflen == 8)
     curve = CURVE_NIST_P256;
-  else
+  else if (buflen == 5 && buffer[5] == 0x0a)
     curve = CURVE_SEC_P256K1;
+  else if (buflen == 9)
+    curve = CURVE_ED25519;
+  else
+    curve = CURVE_UNKNOWN;
 
   return curve;
 }
@@ -4374,10 +4450,17 @@ parse_algorithm_attribute (app_t app, int keyno)
   else if (*buffer == 18 && buflen == 11) /* ECDH */
     {
       app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECDH;
-      app->app_local->keyattr[keyno].ecdh.curve
-        = parse_ecc_curve (buffer + 1, buflen - 1);
       app->app_local->keyattr[keyno].ecdh.hashalgo = buffer[1];
       app->app_local->keyattr[keyno].ecdh.cipheralgo = buffer[2];
+      app->app_local->keyattr[keyno].ecdh.curve
+        = parse_ecc_curve (buffer + 3, buflen - 3);
+    }
+  else if (*buffer == 105) /* EdDSA (experimental) */
+    {
+      app->app_local->keyattr[keyno].key_type = KEY_TYPE_EDDSA;
+      app->app_local->keyattr[keyno].eddsa.hashalgo = buffer[1];
+      app->app_local->keyattr[keyno].eddsa.curve
+        = parse_ecc_curve (buffer + 2, buflen - 2);
     }
   else if (opt.verbose)
     log_printhex ("", buffer, buflen);





More information about the Gnupg-devel mailing list