scd: Support ECC key generation.
authorNIIBE Yutaka <gniibe@fsij.org>
Tue, 18 Oct 2016 13:46:37 +0000 (22:46 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Tue, 18 Oct 2016 13:58:00 +0000 (22:58 +0900)
* scd/app-openpgp.c (get_public_key): Fix a message.
(change_keyattr_from_string, ecc_writekey): Call mpi_release sooner.
(do_genkey): Add ECC support.

--

In OpenPGP card specification 3.0, ECC is introduced.  So far, do_genkey
only supported RSA.  Since KDF spec. is needed to calculate the
fingerprint, it is hard coded in app-openpgp.c.  But it's defined by
OpenPGP ECC (RFC-6637), and card does nothing with KDF in fact.

Co-authored-by: Arnaud Fontaine <arnaud.fontaine@ssi.gouv.fr>
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
scd/app-openpgp.c

index ba16255..09e4800 100644 (file)
@@ -1318,7 +1318,7 @@ get_public_key (app_t app, int keyno)
           if (!m)
             {
               err = gpg_error (GPG_ERR_CARD);
-              log_error (_("response does not contain the EC public point\n"));
+              log_error (_("response does not contain the EC public key\n"));
               goto leave;
             }
         }
@@ -2847,6 +2847,7 @@ change_keyattr_from_string (app_t app,
       size_t oid_len;
 
       oidstr = openpgp_curve_to_oid (string+n, NULL);
+      gcry_mpi_release (oid);
       if (!oidstr)
         {
           err = gpg_error (GPG_ERR_INV_DATA);
@@ -2864,7 +2865,6 @@ change_keyattr_from_string (app_t app,
       string[0] = algo;
       memcpy (string+1, oidbuf+1, oid_len-1);
       err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
-      gcry_mpi_release (oid);
     }
   else
     err = gpg_error (GPG_ERR_PUBKEY_ALGO);
@@ -3355,13 +3355,14 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   if (err)
     goto leave;
   oidbuf = gcry_mpi_get_opaque (oid, &n);
-  oid_len = (n+7)/8;
   if (!oidbuf)
     {
       err = gpg_error_from_syserror ();
       gcry_mpi_release (oid);
       goto leave;
     }
+  gcry_mpi_release (oid);
+  oid_len = (n+7)/8;
 
   if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
       || app->app_local->keyattr[keyno].ecc.oid != oidstr
@@ -3442,8 +3443,6 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
                    ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
 
  leave:
-  if (oidbuf)
-    gcry_mpi_release (oid);
   return err;
 }
 
@@ -3535,16 +3534,15 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   gpg_error_t err;
   char numbuf[30];
   unsigned char fprbuf[20];
-  const unsigned char *keydata, *m, *e;
   unsigned char *buffer = NULL;
-  size_t buflen, keydatalen, mlen, elen;
+  const unsigned char *keydata;
+  size_t buflen, keydatalen;
   u32 created_at;
   int keyno = atoi (keynostr) - 1;
   int force = (flags & 1);
   time_t start_at;
-  int exmode;
-  int le_value;
-  unsigned int keybits;
+  int exmode = 0;
+  int le_value = 256; /* Use legacy value. */
 
   if (keyno < 0 || keyno > 2)
     return gpg_error (GPG_ERR_INV_ID);
@@ -3564,34 +3562,34 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   if (err)
     return err;
 
-  /* Because we send the key parameter back via status lines we need
-     to put a limit on the max. allowed keysize.  2048 bit will
-     already lead to a 527 byte long status line and thus a 4096 bit
-     key would exceed the Assuan line length limit.  */
-  keybits = app->app_local->keyattr[keyno].rsa.n_bits;
-  if (keybits > 4096)
-    return gpg_error (GPG_ERR_TOO_LARGE);
+  if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+    {
+      unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits;
+
+      /* Because we send the key parameter back via status lines we need
+         to put a limit on the max. allowed keysize.  2048 bit will
+         already lead to a 527 byte long status line and thus a 4096 bit
+         key would exceed the Assuan line length limit.  */
+      if (keybits > 4096)
+        return gpg_error (GPG_ERR_TOO_LARGE);
+
+      /* Test whether we will need extended length mode.  (1900 is an
+         arbitrary length which for sure fits into a short apdu.)  */
+      if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
+        {
+          exmode = 1;    /* Use extended length w/o a limit.  */
+          le_value = app->app_local->extcap.max_rsp_data;
+          /* No need to check le_value because it comes from a 16 bit
+             value and thus can't create an overflow on a 32 bit
+             system.  */
+        }
+    }
 
   /* Prepare for key generation by verifying the Admin PIN.  */
   err = verify_chv3 (app, pincb, pincb_arg);
   if (err)
-    goto leave;
+    return err;
 
-  /* Test whether we will need extended length mode.  (1900 is an
-     arbitrary length which for sure fits into a short apdu.)  */
-  if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
-    {
-      exmode = 1;    /* Use extended length w/o a limit.  */
-      le_value = app->app_local->extcap.max_rsp_data;
-      /* No need to check le_value because it comes from a 16 bit
-         value and thus can't create an overflow on a 32 bit
-         system.  */
-    }
-  else
-    {
-      exmode = 0;
-      le_value = 256; /* Use legacy value. */
-    }
 
   log_info (_("please wait while key is being generated ...\n"));
   start_at = time (NULL);
@@ -3601,9 +3599,8 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
                                   2, le_value, &buffer, &buflen);
   if (err)
     {
-      err = gpg_error (GPG_ERR_CARD);
       log_error (_("generating key failed\n"));
-      goto leave;
+      return gpg_error (GPG_ERR_CARD);
     }
 
   {
@@ -3621,38 +3618,117 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
       goto leave;
     }
 
-  m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
-  if (!m)
-    {
-      err = gpg_error (GPG_ERR_CARD);
-      log_error (_("response does not contain the RSA modulus\n"));
-      goto leave;
-    }
-  /* log_printhex ("RSA n:", m, mlen); */
-  send_key_data (ctrl, "n", m, mlen);
-
-  e = find_tlv (keydata, keydatalen, 0x0082, &elen);
-  if (!e)
-    {
-      err = gpg_error (GPG_ERR_CARD);
-      log_error (_("response does not contain the RSA public exponent\n"));
-      goto leave;
-    }
-  /* log_printhex ("RSA e:", e, elen); */
-  send_key_data (ctrl, "e", e, elen);
-
   created_at = (u32)(createtime? createtime : gnupg_get_time ());
   sprintf (numbuf, "%u", created_at);
   send_status_info (ctrl, "KEY-CREATED-AT",
                     numbuf, (size_t)strlen(numbuf), NULL, 0);
 
-  for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
-    ;
-  for (; elen && !*e; elen--, e++) /* strip leading zeroes */
-    ;
+  if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+    {
+      const unsigned char *m, *e;
+      size_t mlen, elen;
+
+      m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+      if (!m)
+        {
+          err = gpg_error (GPG_ERR_CARD);
+          log_error (_("response does not contain the RSA modulus\n"));
+          goto leave;
+        }
+      /* log_printhex ("RSA n:", m, mlen); */
+      send_key_data (ctrl, "n", m, mlen);
+
+      e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+      if (!e)
+        {
+          err = gpg_error (GPG_ERR_CARD);
+          log_error (_("response does not contain the RSA public exponent\n"));
+          goto leave;
+        }
+      /* log_printhex ("RSA e:", e, elen); */
+      send_key_data (ctrl, "e", e, elen);
+
+      for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+        ;
+      for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+        ;
+
+      err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
+                       m, mlen, e, elen);
+    }
+  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
+    {
+      const unsigned char *ecc_q;
+      size_t ecc_q_len;
+      gcry_mpi_t oid;
+      int n;
+      const unsigned char *oidbuf;
+      size_t oid_len;
+      int algo;
+
+      ecc_q = find_tlv (keydata, keydatalen, 0x0086, &ecc_q_len);
+      if (!ecc_q)
+        {
+          err = gpg_error (GPG_ERR_CARD);
+          log_error (_("response does not contain the EC public key\n"));
+          goto leave;
+        }
+
+      err = openpgp_oid_from_str (app->app_local->keyattr[keyno].ecc.oid, &oid);
+      if (err)
+        goto leave;
+
+      oidbuf = gcry_mpi_get_opaque (oid, &n);
+      if (!oidbuf)
+        {
+          err = gpg_error_from_syserror ();
+          gcry_mpi_release (oid);
+          goto leave;
+        }
+      gcry_mpi_release (oid);
+      oid_len = (n+7)/8;
+
+      if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+        {               /* Prepend 0x40 prefix.  */
+          unsigned char *q = xtrymalloc (ecc_q_len + 1);
+
+          if (!q)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          *q = 0x40;
+          memcpy (q+1, ecc_q, ecc_q_len);
+          send_key_data (ctrl, "q", q, ecc_q_len + 1);
+          xfree (q);
+        }
+      else
+        {
+          /* strip leading zeroes */
+          for (; ecc_q_len && !*ecc_q; ecc_q_len--, ecc_q++)
+            ;
+          send_key_data (ctrl, "q", ecc_q, ecc_q_len);
+        }
+
+      send_key_data (ctrl, "curve", oidbuf, oid_len);
+
+      if (keyno == 1)
+        {
+          send_key_data (ctrl, "kdf", "\x03\x01\x08\x07", (size_t)4);
+          algo = PUBKEY_ALGO_ECDH;
+        }
+      else
+        {
+          if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+            algo = PUBKEY_ALGO_EDDSA;
+          else
+            algo = PUBKEY_ALGO_ECDSA;
+        }
+
+      err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+                       ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
+    }
 
-  err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
-                   m, mlen, e, elen);
   if (err)
     goto leave;
   send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);