* cardglue.c (send_status_info): Make CTRL optional.
authorWerner Koch <wk@gnupg.org>
Sat, 21 May 2005 14:04:32 +0000 (14:04 +0000)
committerWerner Koch <wk@gnupg.org>
Sat, 21 May 2005 14:04:32 +0000 (14:04 +0000)
(agent_scd_writekey, inq_writekey_parms): New.
(agent_openpgp_storekey): Removed.
* cardglue.h: Add a few more error code mappings.
* keygen.c (copy_mpi): Removed.
(save_unprotected_key_to_card): Changed to use agent_scd_writekey.
* app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer
version in gnupg 1.9 CVS.

18 files changed:
NEWS
g10/ChangeLog
g10/app-common.h
g10/app-openpgp.c
g10/cardglue.c
g10/cardglue.h
g10/keygen.c
g10/tlv.c
g10/tlv.h
include/ChangeLog
include/util.h
kbx/keybox-blob.c
mpi/ChangeLog
mpi/mpi-scan.c
mpi/mpicoder.c
util/ChangeLog
util/Makefile.am
util/membuf.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 6a58be6..edcc648 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,23 @@
 Noteworthy changes in version 1.4.2
 ------------------------------------------------
 
+    * New command "verify" in the card-edit menu to display
+      the Private-DO-3.  The Admin command has been enhanced to take
+      the optional arguments "on", "off" and "verify".  The latter may
+      be used to verify the ADmin Pin without modifying data; this
+      allows displayin the Private-DO-4 with the "list" command.
+
+    * Rewrote large parts of the card code to optionally make use of a
+      running gpg-agent.  If --use-agent is beeing used and a
+      gpg-agent with enabled scdaemon is active, gpg will now divert
+      all card operations to that daemon.  This is required because
+      bot, scdaemon and gpg require exclusive access to the card
+      reader. By delegating the work to scdaemon, both can peacefully
+      coexist and scdaemon is able to control the use of the reader.
+      Note that this requires at least gnupg 1.9.17.
+
+    * Fixed a couple of problems with the card reader.
+
 
 Noteworthy changes in version 1.4.1 (2005-03-15)
 ------------------------------------------------
index 8f05b7e..fc36891 100644 (file)
@@ -1,3 +1,14 @@
+2005-05-21  Werner Koch  <wk@g10code.com>
+
+       * cardglue.c (send_status_info): Make CTRL optional.
+       (agent_scd_writekey, inq_writekey_parms): New.
+       (agent_openpgp_storekey): Removed.
+       * cardglue.h: Add a few more error code mappings.
+       * keygen.c (copy_mpi): Removed.
+       (save_unprotected_key_to_card): Changed to use agent_scd_writekey.
+       * app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer
+       version in gnupg 1.9 CVS.
+
 2005-05-20  Werner Koch  <wk@g10code.com>
 
        * ccid-driver.c (ccid_transceive): Arghhh.  The seqno is another
index f1058dd..c2c3023 100644 (file)
@@ -86,6 +86,11 @@ struct app_ctx_s {
                      void *pincb_arg,
                      const void *indata, size_t indatalen,
                      unsigned char **outdata, size_t *outdatalen);
+    gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
+                             const char *certid, unsigned int flags,
+                             gpg_error_t (*pincb)(void*,const char *,char **),
+                             void *pincb_arg,
+                             const unsigned char *pk, size_t pklen);
     gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
                    const char *keynostr, unsigned int flags,
                    gpg_error_t (*pincb)(void*, const char *, char **),
@@ -148,6 +153,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr,
                   void *pincb_arg,
                   const void *indata, size_t indatalen,
                   unsigned char **outdata, size_t *outdatalen );
+gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
+                          const char *keyidstr, unsigned int flags,
+                          gpg_error_t (*pincb)(void*, const char *, char **),
+                          void *pincb_arg,
+                          const unsigned char *keydata, size_t keydatalen);
 gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
                 const char *keynostr, unsigned int flags,
                 gpg_error_t (*pincb)(void*, const char *, char **),
index 6f4778b..1165ec6 100644 (file)
@@ -565,7 +565,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
   n = 6 + 2 + mlen + 2 + elen;
   p = buffer = xtrymalloc (3 + n);
   if (!buffer)
-    return gpg_error (gpg_err_code_from_errno (errno));
+    return gpg_error_from_errno (errno);
   
   *p++ = 0x99;     /* ctb */
   *p++ = n >> 8;   /* 2 byte length header */
@@ -1527,6 +1527,318 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, int reset_mode,
 }
 
 
+/* Check whether a key already exists.  KEYIDX is the index of the key
+   (0..2).  If FORCE is TRUE a diagnositivc will be printed but no
+   error returned if the key already exists. */
+static gpg_error_t
+does_key_exist (app_t app, int keyidx, int force)
+{
+  const unsigned char *fpr;
+  unsigned char *buffer;
+  size_t buflen, n;
+  int i;
+
+  assert (keyidx >=0 && keyidx <= 2);
+
+  if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
+    {
+      log_error (_("error reading application data\n"));
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  fpr = find_tlv (buffer, buflen, 0x00C5, &n);
+  if (!fpr || n < 60)
+    {
+      log_error (_("error reading fingerprint DO\n"));
+      xfree (buffer);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  fpr += 20*keyidx;
+  for (i=0; i < 20 && !fpr[i]; i++)
+    ;
+  xfree (buffer);
+  if (i!=20 && !force)
+    {
+      log_error (_("key already exists\n"));
+      return gpg_error (GPG_ERR_EEXIST);
+    }
+  else if (i!=20)
+    log_info (_("existing key will be replaced\n"));
+  else
+    log_info (_("generating new key\n"));
+  return 0;
+}
+
+
+
+/* Handle the WRITEKEY command for OpenPGP.  This function expects a
+   canonical encoded S-expression with the secret key in KEYDATA and
+   its length (for assertions) in KEYDATALEN.  KEYID needs to be the
+   usual keyid which for OpenPGP is the string "OPENPGP.n" with
+   n=1,2,3.  Bit 0 of FLAGS indicates whether an existing key shall
+   get overwritten.  PINCB and PINCB_ARG are the usual arguments for
+   the pinentry callback.  */
+static gpg_error_t
+do_writekey (app_t app, ctrl_t ctrl,
+             const char *keyid, unsigned int flags,
+             gpg_error_t (*pincb)(void*, const char *, char **),
+             void *pincb_arg,
+             const unsigned char *keydata, size_t keydatalen)
+{
+  gpg_error_t err;
+  int force = (flags & 1);
+  int keyno;
+  const unsigned char *buf, *tok;
+  size_t buflen, toklen;
+  int depth, last_depth1, last_depth2;
+  const unsigned char *rsa_n = NULL;
+  const unsigned char *rsa_e = NULL;
+  const unsigned char *rsa_p = NULL;
+  const unsigned char *rsa_q = NULL;
+  size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+  unsigned int nbits;
+  unsigned char *template = NULL;
+  unsigned char *tp;
+  size_t template_len;
+  unsigned char fprbuf[20];
+  u32 created_at = 0;
+
+  if (!strcmp (keyid, "OPENPGP.1"))
+    keyno = 0;
+  else if (!strcmp (keyid, "OPENPGP.2"))
+    keyno = 1;
+  else if (!strcmp (keyid, "OPENPGP.3"))
+    keyno = 2;
+  else
+    return gpg_error (GPG_ERR_INV_ID);
+  
+  err = does_key_exist (app, keyno, force);
+  if (err)
+    return err;
+
+
+  /* 
+     Parse the S-expression
+   */
+  buf = keydata;
+  buflen = keydatalen;
+  depth = 0;
+  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+    goto leave;
+  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+    goto leave;
+  if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
+    {
+      if (!tok)
+        ;
+      else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
+        log_info ("protected-private-key passed to writekey\n");
+      else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
+        log_info ("shadowed-private-key passed to writekey\n");
+      err = gpg_error (GPG_ERR_BAD_SECKEY);
+      goto leave;
+    }
+  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+    goto leave;
+  if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+    goto leave;
+  if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
+    {
+      err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+      goto leave;
+    }
+  last_depth1 = depth;
+  while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+         && depth && depth >= last_depth1)
+    {
+      if (tok)
+        {
+          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+          goto leave;
+        }
+      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+        goto leave;
+      if (tok && toklen == 1)
+        {
+          const unsigned char **mpi;
+          size_t *mpi_len;
+
+          switch (*tok)
+            {
+            case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; 
+            case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; 
+            case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break; 
+            case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break; 
+            default: mpi = NULL;  mpi_len = NULL; break;
+            }
+          if (mpi && *mpi)
+            {
+              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;
+            }
+        }
+      /* Skip until end of list. */
+      last_depth2 = depth;
+      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+             && depth && depth >= last_depth2)
+        ;
+      if (err)
+        goto leave;
+    }
+  /* Parse other attributes. */
+  last_depth1 = depth;
+  while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+         && depth && depth >= last_depth1)
+    {
+      if (tok)
+        {
+          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+          goto leave;
+        }
+      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+        goto leave;
+      if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
+        {
+          if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
+            goto leave;
+          if (tok)
+            {
+              for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
+                   tok++, toklen--)
+                created_at = created_at*10 + (*tok - '0');
+            }
+        }
+      /* Skip until end of list. */
+      last_depth2 = depth;
+      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+             && depth && depth >= last_depth2)
+        ;
+      if (err)
+        goto leave;
+    }
+
+
+  /* Check that we have all parameters and that they match the card
+     description. */
+  if (!created_at)
+    {
+      log_error (_("creation timestamp missing\n"));
+      err = gpg_error (GPG_ERR_INV_VALUE);
+      goto leave;
+    }
+  nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
+  if (nbits != 1024)
+    {
+      log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+      err = gpg_error (GPG_ERR_BAD_SECKEY);
+      goto leave;
+    }
+  nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
+  if (nbits < 2 || nbits > 32)
+    {
+      log_error (_("RSA public exponent missing or largerr than %d bits\n"),
+                 32);
+      err = gpg_error (GPG_ERR_BAD_SECKEY);
+      goto leave;
+    }
+  nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
+  if (nbits != 512)
+    {
+      log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
+      err = gpg_error (GPG_ERR_BAD_SECKEY);
+      goto leave;
+    }
+  nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
+  if (nbits != 512)
+    {
+      log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
+      err = gpg_error (GPG_ERR_BAD_SECKEY);
+      goto leave;
+    }
+  
+
+  /* Build the private key template as described in section 4.3.3.6 of
+     the OpenPGP card specs:
+         0xC0   <length> public exponent
+         0xC1   <length> prime p 
+         0xC2   <length> prime q 
+  */
+  assert (rsa_e_len <= 4);
+  template_len = (1 + 1 + 4
+                  + 1 + 1 + rsa_p_len
+                  + 1 + 1 + rsa_q_len);
+  template = tp = xtrymalloc_secure (template_len);
+  if (!template)
+    {
+      err = gpg_error_from_errno (errno);
+      goto leave;
+    }
+  *tp++ = 0xC0;
+  *tp++ = 4;
+  memcpy (tp, rsa_e, rsa_e_len);
+  if (rsa_e_len < 4)
+    {
+      /* Right justify E. */
+      memmove (tp+4-rsa_e_len, tp, 4-rsa_e_len);
+      memset (tp, 0, 4-rsa_e_len);
+    }                 
+  tp += 4;
+
+  *tp++ = 0xC1;
+  *tp++ = rsa_p_len;
+  memcpy (tp, rsa_p, rsa_p_len);
+  tp += rsa_p_len;
+
+  *tp++ = 0xC2;
+  *tp++ = rsa_q_len;
+  memcpy (tp, rsa_q, rsa_q_len);
+  tp += rsa_q_len;
+
+  assert (tp - template == template_len);
+
+
+  /* Obviously we need to remove the cached public key.  */
+  xfree (app->app_local->pk[keyno].key);
+  app->app_local->pk[keyno].key = NULL;
+  app->app_local->pk[keyno].keylen = 0;
+  app->app_local->pk[keyno].read_done = 0;
+
+  /* Prepare for storing the key.  */
+  err = verify_chv3 (app, pincb, pincb_arg);
+  if (err)
+    goto leave;
+
+  /* Store the key. */
+  err = iso7816_put_data (app->slot,
+                         (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
+                         template, template_len);
+  if (err)
+    {
+      log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+  err = store_fpr (app->slot, keyno, created_at,
+                  rsa_n, rsa_n_len, rsa_e, rsa_e_len,
+                  fprbuf, app->card_version);
+  if (err)
+    goto leave;
+
+
+ leave:
+  xfree (template);
+  return err;
+}
+
 
 /* Handle the GENKEY command. */
 static gpg_error_t 
@@ -1535,13 +1847,11 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
           void *pincb_arg)
 {
   int rc;
-  int i;
   char numbuf[30];
   unsigned char fprbuf[20];
-  const unsigned char *fpr;
   const unsigned char *keydata, *m, *e;
-  unsigned char *buffer;
-  size_t buflen, keydatalen, n, mlen, elen;
+  unsigned char *buffer = NULL;
+  size_t buflen, keydatalen, mlen, elen;
   time_t created_at;
   int keyno = atoi (keynostr);
   int force = (flags & 1);
@@ -1562,41 +1872,15 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   app->app_local->pk[keyno].read_done = 0;
 
   /* Check whether a key already exists.  */
-  rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
+  rc = does_key_exist (app, keyno, force);
   if (rc)
-    {
-      log_error (_("error reading application data\n"));
-      return gpg_error (GPG_ERR_GENERAL);
-    }
-  fpr = find_tlv (buffer, buflen, 0x00C5, &n);
-  if (!fpr || n != 60)
-    {
-      rc = gpg_error (GPG_ERR_GENERAL);
-      log_error (_("error reading fingerprint DO\n"));
-      goto leave;
-    }
-  fpr += 20*keyno;
-  for (i=0; i < 20 && !fpr[i]; i++)
-    ;
-  if (i!=20 && !force)
-    {
-      rc = gpg_error (GPG_ERR_EEXIST);
-      log_error (_("key already exists\n"));
-      goto leave;
-    }
-  else if (i!=20)
-    log_info (_("existing key will be replaced\n"));
-  else
-    log_info (_("generating new key\n"));
+    return rc;
 
-  
   /* Prepare for key generation by verifying the ADmin PIN.  */
   rc = verify_chv3 (app, pincb, pincb_arg);
   if (rc)
     goto leave;
    
-  xfree (buffer); buffer = NULL;
-
 #if 1
   log_info (_("please wait while key is being generated ...\n"));
   start_at = time (NULL);
@@ -2220,6 +2504,7 @@ app_select_openpgp (app_t app)
       app->fnc.readkey = do_readkey;
       app->fnc.getattr = do_getattr;
       app->fnc.setattr = do_setattr;
+      app->fnc.writekey = do_writekey;
       app->fnc.genkey = do_genkey;
       app->fnc.sign = do_sign;
       app->fnc.auth = do_auth;
index 940ec64..01d8b42 100644 (file)
@@ -63,6 +63,15 @@ struct pincb_parm_s
 };
 
 
+struct writekey_parm_s
+{
+  assuan_context_t ctx;
+  const unsigned char *keydata;
+  size_t keydatalen;
+};
+
+
+
 static char *default_reader_port;
 static app_t current_app;
 
@@ -100,7 +109,7 @@ serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
    buffers. The variable elements are pairs of (char *, size_t),
    terminated with a (NULL, 0). */
 void
-send_status_info (CTRL ctrl, const char *keyword, ...)
+send_status_info (ctrl_t ctrl, const char *keyword, ...)
 {
   va_list arg_ptr;
   const unsigned char *value;
@@ -140,7 +149,8 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
         }
     }
   *p = 0;
-  ctrl->status_cb (ctrl->status_cb_arg, buf);
+  if (ctrl && ctrl->status_cb)
+    ctrl->status_cb (ctrl->status_cb_arg, buf);
 
   va_end (arg_ptr);
 }
@@ -970,6 +980,59 @@ agent_scd_setattr (const char *name,
 }
 
 
+/* Handle a KEYDATA inquiry.  Note, we only send the data,
+   assuan_transact takes care of flushing and writing the end */
+static assuan_error_t
+inq_writekey_parms (void *opaque, const char *keyword)
+{
+  struct writekey_parm_s *parm = opaque; 
+
+  return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen);
+}
+
+
+/* Send a WRITEKEY command to the SCdaemon. */
+int 
+agent_scd_writekey (int keyno, const unsigned char *keydata, size_t keydatalen)
+{
+  app_t app;
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  app = current_app? current_app : open_card ();
+  if (!app)
+    return gpg_error (GPG_ERR_CARD);
+
+  if (app->assuan_ctx)
+    {
+      struct writekey_parm_s parms;
+
+      snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno);
+      line[DIM(line)-1] = 0;
+      parms.ctx = app->assuan_ctx;
+      parms.keydata = keydata;
+      parms.keydatalen = keydatalen;
+      rc = test_transact (assuan_transact (app->assuan_ctx, line,
+                                           NULL, NULL,
+                                           inq_writekey_parms, &parms,
+                                           NULL, NULL),
+                          "SCD WRITEKEY");
+    }
+  else
+    {
+      snprintf (line, DIM(line)-1, "OPENPGP.%d", keyno);
+      line[DIM(line)-1] = 0;
+      rc = app->fnc.writekey (app, NULL, line, 0x0001,
+                              pin_cb, NULL,
+                              keydata, keydatalen);
+    }
+
+  if (rc)
+    write_status (STATUS_SC_OP_FAILURE);
+  return rc;
+}
+
+
+
 static assuan_error_t
 genkey_status_cb (void *opaque, const char *line)
 {
@@ -1281,37 +1344,6 @@ agent_scd_checkpin (const char *serialnobuf)
 }
 
 
-/* Wrapper to call the store key helper function of app-openpgp.c.  */
-int 
-agent_openpgp_storekey (int keyno,
-                        unsigned char *template, size_t template_len,
-                        time_t created_at,
-                        const unsigned char *m, size_t mlen,
-                        const unsigned char *e, size_t elen)
-{
-  app_t app;
-  int rc;
-
-  app = current_app? current_app : open_card ();
-  if (!app)
-    return gpg_error (GPG_ERR_CARD);
-
-  if (app->assuan_ctx)
-    {
-      rc = gpg_error (GPG_ERR_CARD);
-    }
-  else
-    {
-      rc = app_openpgp_storekey (app, keyno, template, template_len,
-                                 created_at, m, mlen, e, elen,
-                                 pin_cb, NULL);
-    }
-
-  if (rc)
-    write_status (STATUS_SC_OP_FAILURE);
-  return rc;
-}
-
 
 void
 agent_clear_pin_cache (const char *sn)
index 068a1aa..a679dbb 100644 (file)
@@ -81,6 +81,7 @@ typedef struct ctrl_ctx_s *ctrl_t;
 
 #define GPG_ERR_GENERAL           G10ERR_GENERAL
 #define GPG_ERR_BAD_PIN           G10ERR_BAD_PASS
+#define GPG_ERR_BAD_KEy           G10ERR_BAD_KEY
 #define GPG_ERR_CARD              G10ERR_GENERAL
 #define GPG_ERR_EEXIST            G10ERR_FILE_EXISTS
 #define GPG_ERR_ENOMEM            G10ERR_RESOURCE_LIMIT
@@ -105,6 +106,10 @@ typedef struct ctrl_ctx_s *ctrl_t;
 #define GPG_ERR_EOF               (-1)
 #define GPG_ERR_CARD_NOT_PRESENT  G10ERR_NO_CARD
 #define GPG_ERR_CARD_RESET        G10ERR_GENERAL
+#define GPG_ERR_WRONG_PUBKEY_ALGO G10ERR_PUBKEY_ALGO
+#define GPG_ERR_UNKNOWN_SEXP      G10ERR_INV_ARG
+#define GPG_ERR_DUP_VALUE         G10ERR_INV_ARG
+#define GPG_ERR_BAD_SECKEY        G10ERR_BAD_SECKEY
 
 #define GPG_ERR_EBUSY             G10ERR_GENERAL
 #define GPG_ERR_ENOENT            G10ERR_OPEN_FILE
@@ -129,6 +134,7 @@ typedef int gpg_err_code_t;
 #define xtrymalloc(n)    xmalloc((n))
 #define xtrycalloc(n,m)  xcalloc((n),(m))
 #define xtryrealloc(n,m) xrealloc((n),(m))
+#define xtrymalloc_secure(n)  xmalloc_secure((n))
 #define out_of_core()    (-1) 
 
 #define gnupg_get_time() make_timestamp ()
@@ -168,6 +174,10 @@ int agent_scd_getattr (const char *name, struct agent_card_info_s *info);
 int agent_scd_setattr (const char *name,
                        const unsigned char *value, size_t valuelen);
 
+/* Send a WRITEKEY command to the SCdaemon. */
+int agent_scd_writekey (int keyno,
+                        const unsigned char *keydata, size_t keydatalen);
+
 /* Send a GENKEY command to the SCdaemon. */
 int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force);
 
@@ -187,13 +197,6 @@ int agent_scd_change_pin (int chvno);
 /* Send a CHECKPIN command. */
 int agent_scd_checkpin (const char *serialnobuf);
 
-/* Call the store key utility command. */
-int agent_openpgp_storekey (int keyno,
-                            unsigned char *template, size_t template_len,
-                            time_t created_at,
-                            const unsigned char *m, size_t mlen,
-                            const unsigned char *e, size_t elen);
-
 /* Clear a cached PIN. */
 void agent_clear_pin_cache (const char *sn);
 
index c4b9dab..b5408a6 100644 (file)
@@ -128,42 +128,6 @@ static int gen_card_key_with_backup (int algo, int keyno, int is_primary,
                                      const char *backup_dir);
 
 
-#if GNUPG_MAJOR_VERSION == 1
-#define GET_NBITS(a)  mpi_get_nbits (a)
-#else
-#define GET_NBITS(a)  gcry_mpi_get_nbits (a)
-#endif
-
-#ifdef ENABLE_CARD_SUPPORT
-static int
-copy_mpi (MPI a, unsigned char *buffer, size_t len, size_t *ncopied)
-{
-  int rc;
-#if GNUPG_MAJOR_VERSION == 1
-  unsigned char *tmp;
-  unsigned int n;
-
-  tmp = mpi_get_secure_buffer (a, &n, NULL);
-  if (n > len)
-    rc = G10ERR_GENERAL;
-  else
-    {
-      rc = 0;
-      memcpy (buffer, tmp, n);
-      *ncopied = n;
-    }
-  xfree (tmp);
-#else /* GNUPG_MAJOR_VERSION != 1 */
-  rc = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, len, ncopied, a);
-#endif /* GNUPG_MAJOR_VERSION != 1 */
-  if (rc)
-    log_error ("mpi_copy failed: %s\n", gpg_strerror (rc));
-  return rc;
-}
-#endif /* ENABLE_CARD_SUPPORT */
-
-
-
 static void
 print_status_key_created (int letter, PKT_public_key *pk, const char *handle)
 {
@@ -3527,104 +3491,68 @@ int
 save_unprotected_key_to_card (PKT_secret_key *sk, int keyno)
 {
   int rc;
-  size_t n;
-  MPI rsa_n, rsa_e, rsa_p, rsa_q;
-  unsigned int nbits;
-  unsigned char *template = NULL;
-  unsigned char *tp;
-  unsigned char m[128], e[4];
-  size_t mlen, elen;
+  unsigned char *rsa_n = NULL;
+  unsigned char *rsa_e = NULL;
+  unsigned char *rsa_p = NULL;
+  unsigned char *rsa_q = NULL;
+  unsigned int rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+  unsigned char *sexp = NULL;
+  unsigned char *p;
+  char numbuf[55], numbuf2[50];
 
   assert (is_RSA (sk->pubkey_algo));
   assert (!sk->is_protected);
 
-  rc = -1;
-  /* Some basic checks on the key parameters. */
-  rsa_n = sk->skey[0];
-  rsa_e = sk->skey[1];
-  rsa_p = sk->skey[3];
-  rsa_q = sk->skey[4];
-
-  nbits = GET_NBITS (rsa_n);
-  if (nbits != 1024)
+  /* Copy the parameters into straight buffers. */
+  rsa_n = mpi_get_secure_buffer (sk->skey[0], &rsa_n_len, NULL);
+  rsa_e = mpi_get_secure_buffer (sk->skey[1], &rsa_e_len, NULL);
+  rsa_p = mpi_get_secure_buffer (sk->skey[3], &rsa_p_len, NULL);
+  rsa_q = mpi_get_secure_buffer (sk->skey[4], &rsa_q_len, NULL);
+  if (!rsa_n || !rsa_e || !rsa_p || !rsa_q)
     {
-      log_error (_("length of RSA modulus is not %d\n"), 1024);
-      goto leave;
-    }
-  nbits = GET_NBITS (rsa_e);
-  if (nbits < 2 || nbits > 32)
-    {
-      log_error (_("public exponent too large (more than 32 bits)\n"));
-      goto leave;
-    }
-  nbits = GET_NBITS (rsa_p);
-  if (nbits != 512)
-    {
-      log_error (_("length of an RSA prime is not %d\n"), 512);
-      goto leave;
-    }
-  nbits = GET_NBITS (rsa_q);
-  if (nbits != 512)
-    {
-      log_error (_("length of an RSA prime is not %d\n"), 512);
+      rc = G10ERR_INV_ARG;
       goto leave;
     }
 
-  
-  /* We need the modulus later to calculate the fingerprint. */
-  rc = copy_mpi (rsa_n, m, 128, &n);
-  if (rc)
-    goto leave;
-  assert (n == 128);
-  mlen = 128;
-
-  /* Build the private key template as described in section 4.3.3.6 of
-     the OpenPGP card specs:
-         0xC0   <length> public exponent
-         0xC1   <length> prime p 
-         0xC2   <length> prime q 
-  */
-  template = tp = xmalloc_secure (1+2 + 1+1+4 + 1+1+(512/8) + 1+1+(512/8));
-  *tp++ = 0xC0;
-  *tp++ = 4;
-  rc = copy_mpi (rsa_e, tp, 4, &n);
-  if (rc)
-    goto leave;
-  assert (n <= 4);
-  memcpy (e, tp, n);  /* Save a copy of the exponent for later use.  */
-  elen = n;
-  if (n != 4)
-    {
-      memmove (tp+4-n, tp, 4-n);
-      memset (tp, 0, 4-n);
-    }                 
-  tp += 4;
-
-  *tp++ = 0xC1;
-  *tp++ = 64;
-  rc = copy_mpi (rsa_p, tp, 64, &n);
-  if (rc)
-    goto leave;
-  assert (n == 64);
-  tp += 64;
+  /* Put the key into an S-expression. */
+  sexp = p = xmalloc_secure (30
+                             + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len
+                             + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20);
 
-  *tp++ = 0xC2;
-  *tp++ = 64;
-  rc = copy_mpi (rsa_q, tp, 64, &n);
-  if (rc)
-    goto leave;
-  assert (n == 64);
-  tp += 64;
-  assert (tp - template == 138);
+  p = stpcpy (p,"(11:private-key(3:rsa(1:n");
+  sprintf (numbuf, "%u:", rsa_n_len);
+  p = stpcpy (p, numbuf);
+  memcpy (p, rsa_n, rsa_n_len);
+  p += rsa_n_len;
+
+  sprintf (numbuf, ")(1:e%u:", rsa_e_len);
+  p = stpcpy (p, numbuf);
+  memcpy (p, rsa_e, rsa_e_len);
+  p += rsa_e_len;
+
+  sprintf (numbuf, ")(1:p%u:", rsa_p_len);
+  p = stpcpy (p, numbuf);
+  memcpy (p, rsa_p, rsa_p_len);
+  p += rsa_p_len;
+
+  sprintf (numbuf, ")(1:q%u:", rsa_q_len);
+  p = stpcpy (p, numbuf);
+  memcpy (p, rsa_q, rsa_q_len);
+  p += rsa_q_len;
+
+  p = stpcpy (p,"))(10:created-at");
+  sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp);
+  sprintf (numbuf, "%d:", strlen (numbuf2));
+  p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))");
 
-  rc = agent_openpgp_storekey (keyno,
-                               template, tp - template,
-                               sk->timestamp,
-                               m, mlen,
-                               e, elen);
+  rc = agent_scd_writekey (keyno, sexp, p - sexp);
 
  leave:
-  xfree (template);
+  xfree (sexp);
+  xfree (rsa_n);
+  xfree (rsa_e);
+  xfree (rsa_p);
+  xfree (rsa_q);
   return rc;
 }
 #endif /*ENABLE_CARD_SUPPORT*/
index 3a81ea6..b5dcd40 100644 (file)
--- a/g10/tlv.c
+++ b/g10/tlv.c
@@ -221,3 +221,76 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
   *size = length;
   return 0;
 }
+
+
+/* FIXME: The following function should not go into this file but for
+   now it is easier to keep it here. */
+
+/* Return the next token of an canconical encoded S-expression.  BUF
+   is the pointer to the S-expression and BUFLEN is a pointer to the
+   length of this S-expression (used to validate the syntax).  Both
+   are updated to reflect the new position.  The token itself is
+   returned as a pointer into the orginal buffer at TOK and TOKLEN.
+   If a parentheses is the next token, TOK will be set to NULL.
+   TOKLEN is checked to be within the bounds.  On error a error code
+   is returned and all pointers should are not guaranteed to point to
+   a meanigful value. DEPTH should be initialized to 0 and will
+   reflect on return the actual depth of the tree. To detect the end
+   of the S-expression it is advisable to check DEPTH after a
+   successful return:
+
+   depth = 0;
+   while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+          && depth)
+     process_token (tok, toklen);
+   if (err)  
+     handle_error ();
+ */
+gpg_error_t
+parse_sexp (unsigned char const **buf, size_t *buflen,
+            int *depth, unsigned char const **tok, size_t *toklen)
+{
+  const unsigned char *s;
+  size_t n, vlen;
+
+  s = *buf;
+  n = *buflen;
+  *tok = NULL;
+  *toklen = 0;
+  if (!n)
+    return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
+  if (*s == '(')
+    {
+      s++; n--;
+      (*depth)++;
+      *buf = s;
+      *buflen = n;
+      return 0;
+    }
+  if (*s == ')')
+    {
+      if (!*depth)
+        return gpg_error (GPG_ERR_INV_SEXP);
+      *toklen = 1;
+      s++; n--;
+      (*depth)--;
+      *buf = s;
+      *buflen = n;
+      return 0;
+    }
+  for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
+    vlen = vlen*10 + (*s - '0');
+  if (!n || *s != ':')
+    return gpg_error (GPG_ERR_INV_SEXP);
+  s++; n--;
+  if (vlen > n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  *tok = s;
+  *toklen = vlen;
+  s += vlen;
+  n -= vlen;
+  *buf = s;
+  *buflen = n;
+  return 0;
+}
+
index 6285804..f587dd9 100644 (file)
--- a/g10/tlv.h
+++ b/g10/tlv.h
@@ -88,4 +88,21 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
 
 
 
+/* Return the next token of an canconical encoded S-expression.  BUF
+   is the pointer to the S-expression and BUFLEN is a pointer to the
+   length of this S-expression (used to validate the syntax).  Both
+   are updated to reflect the new position.  The token itself is
+   returned as a pointer into the orginal buffer at TOK and TOKLEN.
+   If a parentheses is the next token, TOK will be set to NULL.
+   TOKLEN is checked to be within the bounds.  On error a error code
+   is returned and all pointers should are not guaranteed to point to
+   a meanigful value. DEPTH should be initialized to 0 and will
+   reflect on return the actual depth of the tree. To detect the end
+   of the S-expression it is advisable to check DEPTH after a
+   successful return. */
+gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen,
+                        int *depth, unsigned char const **tok, size_t *toklen);
+
+
+
 #endif /* SCD_TLV_H */
index 4281050..e17b96d 100644 (file)
@@ -1,3 +1,7 @@
+2005-05-19  Werner Koch  <wk@g10code.com>
+
+       * util.h: Add definitions for membuf functions.
+
 2005-05-05  David Shaw  <dshaw@jabberwocky.com>
 
        * util.h: Remove add_days_to_timestamp as unused.
index 97943af..b32d771 100644 (file)
@@ -222,6 +222,24 @@ int strncasecmp (const char *, const char *b, size_t n);
 #define memmove(d, s, n) bcopy((s), (d), (n))
 #endif
 
+/*-- membuf.c --*/
+/* The definition of the structure is private, we only need it here,
+   so it can be allocated on the stack. */
+struct private_membuf_s {
+  size_t len;      
+  size_t size;     
+  char *buf;       
+  int out_of_core; 
+};
+
+typedef struct private_membuf_s membuf_t;
+
+void init_membuf (membuf_t *mb, int initiallen);
+void put_membuf  (membuf_t *mb, const void *buf, size_t len);
+void *get_membuf (membuf_t *mb, size_t *len);
+
+
+
 #if defined (_WIN32)
 /*-- w32reg.c --*/
 char *read_w32_registry_string( const char *root,
@@ -232,7 +250,8 @@ int write_w32_registry_string(const char *root, const char *dir,
 /*-- strgutil.c --*/
 int vasprintf (char **result, const char *format, va_list args);
 int asprintf (char **buf, const char *fmt, ...);
-#endif
+#endif /*_WIN32*/
+
 
 /**** other missing stuff ****/
 #ifndef HAVE_ATEXIT  /* For SunOS */
index 5ad1d26..8a85e02 100644 (file)
@@ -57,7 +57,7 @@ X.509 specific are noted like [X.509: xxx]
    b20 The keys fingerprint
        (fingerprints are always 20 bytes, MD5 left padded with zeroes)
    u32 offset to the n-th key's keyID (a keyID is always 8 byte)
-        or 0 if not known which is the case opnly for X509.
+        or 0 if not known which is the case only for X509.
    u16 special key flags
         bit 0 =
    u16 reserved
@@ -72,7 +72,7 @@ X.509 specific are noted like [X.509: xxx]
         bit 0 =
    byte validity
    byte reserved
-   [For X509, the first user ID is the ISsuer, the second the subject
+   [For X509, the first user ID is the issuer, the second the subject
    and the others are subjectAltNames]
  u16  number of signatures
  u16  size of signature information (4)
@@ -99,7 +99,7 @@ X.509 specific are noted like [X.509: xxx]
 
  b16   MD5 checksum  (useful for KS syncronisation), we might also want to use
     a mac here.
- b4    resevered
+ b4    reserved
 
 */
 
index 00e0e3e..775f437 100644 (file)
@@ -1,3 +1,12 @@
+2005-05-06  Werner Koch  <wk@g10code.com>
+
+       * mpi-scan.c (mpi_putbyte, mpi_getbyte): Removed.  Not used.
+
+2005-04-21  Werner Koch  <wk@g10code.com>
+
+       * mpicoder.c (mpi_read): Changed error detection to always return
+       an error while maintaining the actual number of bytes read.
+
 2005-03-11  Werner Koch  <wk@g10code.com>
 
        * Makefile.am (ASFLAGS): Renamed to AM_CCASFLAGS and added the
index 615b946..fe093ad 100644 (file)
@@ -18,8 +18,8 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
-#include <config.h>
-#include <stdio.h>
+#include <config.h> 
+#include <stdio.h> 
 #include <stdlib.h>
 #include "mpi-internal.h"
 #include "longlong.h"
@@ -31,6 +31,7 @@
  *
  * FIXME: This code is VERY ugly!
  */
+#if 0 /* Code is not used */
 int
 mpi_getbyte( MPI a, unsigned idx )
 {
@@ -48,14 +49,19 @@ mpi_getbyte( MPI a, unsigned idx )
     }
     return -1;
 }
+#endif /* Code is not used */
 
 
 /****************
  * Put a value at position IDX into A. idx counts from lsb to msb
  */
+/* FIXME: There is a problem with the long constants which should have
+a LL prefix or better the macros we use at other places. */
+#if 0 /* Code is not used */
 void
 mpi_putbyte( MPI a, unsigned idx, int xc )
 {
+
     int i, j;
     unsigned n;
     mpi_ptr_t ap;
@@ -104,12 +110,13 @@ mpi_putbyte( MPI a, unsigned idx, int xc )
     }
     abort(); /* index out of range */
 }
+#endif /* Code is not used */
 
 
 /****************
  * Count the number of zerobits at the low end of A
  */
-unsigned
+unsigned int
 mpi_trailing_zeros( MPI a )
 {
     unsigned n, count = 0;
index 4d4e455..ab91386 100644 (file)
@@ -1,5 +1,5 @@
 /* mpicoder.c  -  Coder for the external representation of MPIs
- * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -74,20 +74,23 @@ mpi_read(IOBUF inp, unsigned *ret_nread, int secure)
 #endif
 {
     int c, i, j;
+    unsigned int nmax = *ret_nread;
     unsigned nbits, nbytes, nlimbs, nread=0;
     mpi_limb_t a;
     MPI val = MPI_NULL;
 
     if( (c = iobuf_get(inp)) == -1 )
        goto leave;
-    nread++;
+    if (++nread >= nmax)
+        goto overflow;
     nbits = c << 8;
     if( (c = iobuf_get(inp)) == -1 )
        goto leave;
-    nread++;
+    if (++nread >= nmax)
+        goto overflow;
     nbits |= c;
     if( nbits > MAX_EXTERN_MPI_BITS ) {
-       log_error("mpi too large (%u bits)\n", nbits);
+       log_error("mpi too large for this implementation (%u bits)\n", nbits);
        goto leave;
     }
 
@@ -108,6 +111,15 @@ mpi_read(IOBUF inp, unsigned *ret_nread, int secure)
     for( ; j > 0; j-- ) {
        a = 0;
        for(; i < BYTES_PER_MPI_LIMB; i++ ) {
+            if (nread >= nmax) {
+#ifdef M_DEBUG
+                mpi_debug_free (val);
+#else
+                mpi_free (val);
+#endif
+                val = NULL;
+                goto overflow;
+            }
            a <<= 8;
            a |= iobuf_get(inp) & 0xff; nread++;
        }
@@ -116,10 +128,11 @@ mpi_read(IOBUF inp, unsigned *ret_nread, int secure)
     }
 
   leave:
-    if( nread > *ret_nread )
-       log_bug("mpi crosses packet border\n");
-    else
-       *ret_nread = nread;
+    *ret_nread = nread;
+    return val;
+  overflow:
+    log_error ("mpi larger than indicated length (%u bytes)\n", nmax);
+    *ret_nread = nread;
     return val;
 }
 
index e16df58..f1b36ff 100644 (file)
@@ -1,3 +1,7 @@
+2005-05-19  Werner Koch  <wk@g10code.com>
+
+       * membuf.c: New.  Taken from gnupg 1.9.
+
 2005-05-05  David Shaw  <dshaw@jabberwocky.com>
 
        * miscutil.c (add_days_to_timestamp): Remove as unused.
index cb241da..d54aa7a 100644 (file)
@@ -39,7 +39,7 @@ endif
 libutil_a_SOURCES = logger.c fileutil.c miscutil.c strgutil.c  \
                     ttyio.c  argparse.c memory.c secmem.c errors.c iobuf.c \
                     dotlock.c http.c srv.h srv.c simple-gettext.c \
-                    w32reg.c $(assuan_source)
+                    membuf.c w32reg.c $(assuan_source)
 
 libutil_a_DEPENDENCIES = @LIBOBJS@ @REGEX_O@
 # LIBOBJS is for the replacement functions
diff --git a/util/membuf.c b/util/membuf.c
new file mode 100644 (file)
index 0000000..ea69ee9
--- /dev/null
@@ -0,0 +1,83 @@
+/* membuf.c - A simple implementation of a dynamic buffer
+ *     Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "util.h"
+
+
+/* A simple implementation of a dynamic buffer.  Use init_membuf() to
+   create a buffer, put_membuf to append bytes and get_membuf to
+   release and return the buffer.  Allocation errors are detected but
+   only returned at the final get_membuf(), this helps not to clutter
+   the code with out of core checks.  */
+
+void
+init_membuf (membuf_t *mb, int initiallen)
+{
+  mb->len = 0;
+  mb->size = initiallen;
+  mb->out_of_core = 0;
+  mb->buf = xmalloc (initiallen);
+  if (!mb->buf)
+      mb->out_of_core = errno;
+}
+
+
+void
+put_membuf (membuf_t *mb, const void *buf, size_t len)
+{
+  if (mb->out_of_core)
+    return;
+
+  if (mb->len + len >= mb->size)
+    {
+      char *p;
+      
+      mb->size += len + 1024;
+      p = xrealloc (mb->buf, mb->size);
+      mb->buf = p;
+    }
+  memcpy (mb->buf + mb->len, buf, len);
+  mb->len += len;
+}
+
+
+void *
+get_membuf (membuf_t *mb, size_t *len)
+{
+  char *p;
+
+  if (mb->out_of_core)
+    {
+      xfree (mb->buf);
+      mb->buf = NULL;
+      return NULL;
+    }
+
+  p = mb->buf;
+  *len = mb->len;
+  mb->buf = NULL;
+  mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
+  return p;
+}