[svn] GnuPG - r5433 - in trunk: . agent common dirmngr doc g10 scd sm

svn author wk cvs at cvs.gnupg.org
Fri Oct 1 22:34:00 CEST 2010


Author: wk
Date: 2010-10-01 22:33:53 +0200 (Fri, 01 Oct 2010)
New Revision: 5433

Modified:
   trunk/NEWS
   trunk/README.maint
   trunk/agent/ChangeLog
   trunk/agent/agent.h
   trunk/agent/call-pinentry.c
   trunk/agent/command-ssh.c
   trunk/agent/command.c
   trunk/agent/cvt-openpgp.c
   trunk/agent/cvt-openpgp.h
   trunk/agent/divert-scd.c
   trunk/agent/findkey.c
   trunk/agent/genkey.c
   trunk/agent/gpg-agent.c
   trunk/agent/keyformat.txt
   trunk/agent/pkdecrypt.c
   trunk/agent/pksign.c
   trunk/agent/protect.c
   trunk/agent/t-protect.c
   trunk/common/ChangeLog
   trunk/common/http.c
   trunk/common/logging.c
   trunk/common/sexp-parse.h
   trunk/common/status.c
   trunk/common/util.h
   trunk/configure.ac
   trunk/dirmngr/ChangeLog
   trunk/dirmngr/validate.c
   trunk/doc/ChangeLog
   trunk/doc/DETAILS
   trunk/doc/Makefile.am
   trunk/doc/debugging.texi
   trunk/doc/gpg-agent.texi
   trunk/doc/gpg.texi
   trunk/doc/gpgsm.texi
   trunk/g10/ChangeLog
   trunk/g10/build-packet.c
   trunk/g10/call-agent.c
   trunk/g10/call-agent.h
   trunk/g10/card-util.c
   trunk/g10/compress.c
   trunk/g10/decrypt-data.c
   trunk/g10/decrypt.c
   trunk/g10/encrypt.c
   trunk/g10/export.c
   trunk/g10/free-packet.c
   trunk/g10/getkey.c
   trunk/g10/gpg.c
   trunk/g10/gpgv.c
   trunk/g10/import.c
   trunk/g10/keydb.h
   trunk/g10/keyedit.c
   trunk/g10/keygen.c
   trunk/g10/keyid.c
   trunk/g10/keylist.c
   trunk/g10/keyserver-internal.h
   trunk/g10/keyserver.c
   trunk/g10/main.h
   trunk/g10/mainproc.c
   trunk/g10/options.h
   trunk/g10/packet.h
   trunk/g10/parse-packet.c
   trunk/g10/photoid.c
   trunk/g10/pkclist.c
   trunk/g10/server.c
   trunk/g10/sign.c
   trunk/g10/verify.c
   trunk/scd/ccid-driver.c
   trunk/sm/ChangeLog
   trunk/sm/call-agent.c
   trunk/sm/certchain.c
   trunk/sm/gpgsm.c
   trunk/sm/import.c
Log:
Exporting secret keys via gpg-agent is now basically supported.
A couple of forward ported changes.
Doc updates.


[The diff below has been truncated]

Modified: trunk/agent/ChangeLog
===================================================================
--- trunk/agent/ChangeLog	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/ChangeLog	2010-10-01 20:33:53 UTC (rev 5433)
@@ -1,3 +1,47 @@
+2010-09-30  Werner Koch  <wk at g10code.com>
+
+	* gpg-agent.c (agent_exit): Run cleanup.
+	(cleanup): Run only once.
+
+	* call-pinentry.c (close_button_status_cb): New.
+	(agent_askpin): Add arg R_CANCEL_ALL.  Change all callers.
+	* genkey.c (agent_ask_new_passphrase): Ditto.
+	* findkey.c (unprotect): Return GPG_ERR_FULLY_CANCELED if needed.
+
+	* command.c (cmd_export_key): Add support for OpenPGP keys.
+	* findkey.c (unprotect): Add optional arg R_PASSPHRASE.
+	(agent_key_from_file): Ditto.  Change all callers.
+
+	* findkey.c (unprotect): Do not put the passphrase into the cache
+	if it has been changed.
+
+	* cvt-openpgp.c (convert_to_openpgp, apply_protection)
+	(key_from_sexp): New.
+
+2010-09-29  Werner Koch  <wk at g10code.com>
+
+	* cvt-openpgp.c (convert_openpgp): Rename to convert_from_openpgp.
+
+	* command.c (has_option): Stop at "--".
+	(has_option_name, option_value): Ditto.
+	(skip_options): Skip initial spaces.
+
+2010-09-24  Werner Koch  <wk at g10code.com>
+
+	* gpg-agent.c (main, reread_configuration): Always test whether
+	the default configuration file has been created in the meantime.
+	Fixes bug#1285.
+
+2010-09-17  Werner Koch  <wk at g10code.com>
+
+	* command.c (cmd_havekey): Allow testing of several keygrips.
+
+2010-09-15  Werner Koch  <wk at g10code.com>
+
+	* protect.c (calculate_mic): Take care of shared secret format.
+
+	* agent.h (PROTECTED_SHARED_SECRET): New.
+
 2010-09-02  Werner Koch  <wk at g10code.com>
 
 	* cache.c (new_data): Change arg and callers to use a string and

Modified: trunk/common/ChangeLog
===================================================================
--- trunk/common/ChangeLog	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/common/ChangeLog	2010-10-01 20:33:53 UTC (rev 5433)
@@ -1,3 +1,17 @@
+2010-09-30  Werner Koch  <wk at g10code.com>
+
+	* util.h (GPG_ERR_FULLY_CANCELED): Add replacement.
+
+2010-09-17  Werner Koch  <wk at g10code.com>
+
+	* http.c (INADDR_NONE): Provide fallback.
+	* logging.c (INADDR_NONE): Ditto.
+
+2010-09-16  Werner Koch  <wk at g10code.com>
+
+	* util.h: Add GPG_ERR_MISSING_ISSUER_CERT.
+	* status.c (get_inv_recpsgnr_code): Ditto.
+
 2010-09-13  Werner Koch  <wk at g10code.com>
 
 	* homedir.c (gnupg_bindir) [W32CE]: Change to bin/.

Modified: trunk/dirmngr/ChangeLog
===================================================================
--- trunk/dirmngr/ChangeLog	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/dirmngr/ChangeLog	2010-10-01 20:33:53 UTC (rev 5433)
@@ -1,3 +1,7 @@
+2010-09-16  Werner Koch  <wk at g10code.com>
+
+	* validate.c (validate_cert_chain): Use GPG_ERR_MISSING_ISSUER_CERT.
+
 2010-08-13  Werner Koch  <wk at g10code.com>
 
 	* Makefile.am (dirmngr_SOURCES): Add w32-ldap-help.h.

Modified: trunk/doc/ChangeLog
===================================================================
--- trunk/doc/ChangeLog	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/doc/ChangeLog	2010-10-01 20:33:53 UTC (rev 5433)
@@ -1,3 +1,7 @@
+2010-09-28  Werner Koch  <wk at g10code.com>
+
+	* Makefile.am (AM_MAKEINFOFLAGS): Add define gpgtwoone.
+
 2010-09-28  David Shaw  <dshaw at jabberwocky.com>
 
 	* gpg.texi (OpenPGP Options): Clarify that --force-v3-sigs

Modified: trunk/g10/ChangeLog
===================================================================
--- trunk/g10/ChangeLog	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/g10/ChangeLog	2010-10-01 20:33:53 UTC (rev 5433)
@@ -1,3 +1,67 @@
+2010-10-01  Werner Koch  <wk at g10code.com>
+
+	* export.c (do_export_stream): Rewrite to take the secret keys
+	from the agent.
+	(canon_pubkey_algo, transfer_format_to_openpgp): New.
+
+2010-09-29  Werner Koch  <wk at g10code.com>
+
+	* keygen.c (key_from_sexp): Fix memory leak in the error case.
+
+	* call-agent.c (agent_export_key): New.
+
+2010-09-29  Werner Koch  <wk at g10code.com>
+
+	* build-packet.c (build_packet): Fix up the pkttype.
+
+	* keyid.c (keystr_with_sub): Make SUB_KID optional.
+	(keystr_from_pk_with_sub): Ditto.
+
+	* call-agent.c (agent_scd_pksign): Add missing space.
+
+	* mainproc.c (struct mainproc_context): Add field CTRL.
+	(proc_packets): Add arg CTRL.  Change all callers.
+	(proc_signature_packets, proc_signature_packets_by_fd)
+	(proc_encryption_packets): Add arg CTRL.  Change all callers.
+	* compress.c (handle_compressed): Ditto.
+	* getkey.c (get_pubkey_byname): Ditto.
+	* keyserver.c (keyserver_spawn, keyserver_work): Ditto.
+	(show_prompt, keyserver_export, keyserver_import)
+	(keyserver_import_fprint, keyserver_import_keyid)
+	(keyserver_refresh, keyserver_search, keyserver_fetch)
+	(keyserver_import_name, keyserver_search_prompt)
+	(keyserver_import_pka, keyserver_import_cert): Ditto.
+	callers.
+	* verify.c (verify_signatures, verify_files): Ditto.
+	* sign.c (sign_file): Ditto.
+	* encrypt.c (encrypt_crypt, encrypt_crypt_files): Ditto.
+	* pkclist.c (find_and_check_key, build_pk_list): Ditto.
+	* keylist.c (locate_one, public_key_list, secret_key_list): Ditto.
+	* card-util.c (fetch_url, card_edit): Ditto.
+	* import.c (check_prefs, import_one, revocation_present): Ditto.
+	* keyedit.c (menu_addrevoker, keyedit_menu): Ditto.
+	* decrypt-data.c (decrypt_data): Ditto.
+	* decrypt.c (decrypt_message, decrypt_messages)
+	(decrypt_message_fd): Ditto.
+	* gpgv.c (main): Add CTRL structure.
+
+2010-09-28  Werner Koch  <wk at g10code.com>
+
+	* options.h (struct opt): Remove SIMPLE_SK_CHECKSUM.
+
+	* export.c (parse_export_options): Remove option
+	export-resert-subkey-passwd.
+	(do_export_stream, do_export, export_pubkeys)
+	(export_pubkeys_stream, export_seckeys, export_secsubkeys): Add
+	arg CTRL.  Change all callers.
+
+	* call-agent.c (hash_algo_option): New.
+	(agent_scd_pksign): Use it.
+
+2010-09-17  Werner Koch  <wk at g10code.com>
+
+	* call-agent.c (agent_probe_any_secret_key): New.
+
 2010-09-28  David Shaw  <dshaw at jabberwocky.com>
 
 	* options.skel: Make the example for force-v3-sigs match

Modified: trunk/sm/ChangeLog
===================================================================
--- trunk/sm/ChangeLog	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/sm/ChangeLog	2010-10-01 20:33:53 UTC (rev 5433)
@@ -1,3 +1,13 @@
+2010-09-16  Werner Koch  <wk at g10code.com>
+
+	* certchain.c (gpgsm_walk_cert_chain): Use GPG_ERR_MISSING_ISSUER_CERT.
+	(do_validate_chain): Ditto.
+	(gpgsm_basic_cert_check): Ditto.
+	* call-agent.c (learn_cb): Take care of new
+	GPG_ERR_MISSING_ISSUER_CERT.
+	* import.c (check_and_store): Ditto.
+	(check_and_store): Ditto.
+
 2010-08-16  Werner Koch  <wk at g10code.com>
 
 	* gpgsm.c (main) <aGPGConfList>: Use es_printf.

Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/NEWS	2010-10-01 20:33:53 UTC (rev 5433)
@@ -30,8 +30,8 @@
    by default.
 
  * Dirmngr is now a part of this package.  Dirmngr is now also
-   expected to run as a system service and the configuraion
-   directories are changed to the gnupg name space.
+   expected to run as a system service and the configuration
+   directories are changed to the GnuPG name space.
 
  * Given sufficient permissions Dirmngr is started automagically.
 
@@ -43,7 +43,11 @@
 
  * The OpenPGP import command is now able to merge secret keys.
 
+ * Removed options:
+    --export-options: export-secret-subkey-passwd
+    --simple-sk-checksum
 
+
 Noteworthy changes in version 2.0.13 (2009-09-04)
 -------------------------------------------------
 

Modified: trunk/README.maint
===================================================================
--- trunk/README.maint	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/README.maint	2010-10-01 20:33:53 UTC (rev 5433)
@@ -23,6 +23,7 @@
     (Mainly config.guess and config.sub).
   * [1.4 only] Update gpg.texi and gpgv.texi from the trunk:
        make -C doc update-source-from-gnupg-2
+  * [1.4 and 2.0] Copy needed texinfo files from trunk.
   * Run "make -C po update-po".
   * Write NEWS entries and set the release date in NEWS.
   * In configure.ac set "my_issvn" to "no".

Modified: trunk/agent/agent.h
===================================================================
--- trunk/agent/agent.h	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/agent.h	2010-10-01 20:33:53 UTC (rev 5433)
@@ -182,7 +182,8 @@
     PRIVATE_KEY_UNKNOWN = 0,
     PRIVATE_KEY_CLEAR = 1,
     PRIVATE_KEY_PROTECTED = 2,
-    PRIVATE_KEY_SHADOWED = 3
+    PRIVATE_KEY_SHADOWED = 3,
+    PROTECTED_SHARED_SECRET = 4
   };
 
 
@@ -233,7 +234,8 @@
                                  unsigned char **shadow_info,
                                  cache_mode_t cache_mode,
                                  lookup_ttl_t lookup_ttl,
-                                 gcry_sexp_t *result);
+                                 gcry_sexp_t *result,
+                                 char **r_passphrase);
 gpg_error_t agent_public_key_from_file (ctrl_t ctrl, 
                                         const unsigned char *grip,
                                         gcry_sexp_t *result);
@@ -251,7 +253,7 @@
 int agent_askpin (ctrl_t ctrl,
                   const char *desc_text, const char *prompt_text,
                   const char *inital_errtext,
-                  struct pin_entry_info_s *pininfo);
+                  struct pin_entry_info_s *pininfo, int *r_cancelall);
 int agent_get_passphrase (ctrl_t ctrl, char **retpass,
                           const char *desc, const char *prompt,
                           const char *errtext, int with_qualitybar);
@@ -289,7 +291,7 @@
 /*-- genkey.c --*/
 int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent);
 gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
-                                      char **r_passphrase);
+                                      char **r_passphrase, int *r_cancelall);
 int agent_genkey (ctrl_t ctrl, const char *cache_nonce,
                   const char *keyparam, size_t keyparmlen, membuf_t *outbuf);
 int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);

Modified: trunk/agent/call-pinentry.c
===================================================================
--- trunk/agent/call-pinentry.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/call-pinentry.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -696,6 +696,29 @@
 }
 
 
+/* Check the button_info line for a close action.  */
+static gpg_error_t
+close_button_status_cb (void *opaque, const char *line)
+{
+  int *flag = opaque;
+  const char *keyword = line;
+  int keywordlen;
+
+  for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+    ;
+  while (spacep (line))
+    line++;
+  if (keywordlen == 11 && !memcmp (keyword, "BUTTON_INFO", keywordlen))
+    {
+      if ( !strcmp (line, "close") )
+        *flag = 1;
+    }
+  
+  return 0;
+}
+
+
+
 
 /* Call the Entry and ask for the PIN.  We do check for a valid PIN
    number here and repeat it as long as we have invalid formed
@@ -704,7 +727,7 @@
 agent_askpin (ctrl_t ctrl,
               const char *desc_text, const char *prompt_text,
               const char *initial_errtext,
-              struct pin_entry_info_s *pininfo)
+              struct pin_entry_info_s *pininfo, int *r_cancel_all)
 {
   int rc;
   char line[ASSUAN_LINELENGTH];
@@ -712,6 +735,10 @@
   const char *errtext = NULL;
   int is_pin = 0;
   int saveflag;
+  int close_button;
+  
+  if (r_cancel_all)
+    *r_cancel_all = 0;
 
   if (opt.batch)
     return 0; /* fixme: we should return BAD PIN */
@@ -791,8 +818,10 @@
       
       saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
       assuan_begin_confidential (entry_ctx);
+      close_button = 0;
       rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
-                            inq_quality, entry_ctx, NULL, NULL);
+                            inq_quality, entry_ctx,
+                            close_button_status_cb, &close_button);
       assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
       /* Most pinentries out in the wild return the old Assuan error code
          for canceled which gets translated to an assuan Cancel error and
@@ -801,6 +830,11 @@
           && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
         rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
 
+      /* Set a flag in case the window close button was clicked to
+         cancel the operation.  */
+      if (close_button && r_cancel_all && gpg_err_code (rc) == GPG_ERR_CANCELED)
+        *r_cancel_all = 1;
+
       if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA)
         errtext = is_pin? _("PIN too long")
                         : _("Passphrase too long");

Modified: trunk/agent/command-ssh.c
===================================================================
--- trunk/agent/command-ssh.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/command-ssh.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -2425,7 +2425,7 @@
   pi2->check_cb_arg = pi->pin;
 
  next_try:
-  err = agent_askpin (ctrl, description, NULL, initial_errtext, pi);
+  err = agent_askpin (ctrl, description, NULL, initial_errtext, pi, NULL);
   initial_errtext = NULL;
   if (err)
     goto out;
@@ -2433,7 +2433,7 @@
   /* Unless the passphrase is empty, ask to confirm it.  */
   if (pi->pin && *pi->pin)
     {
-      err = agent_askpin (ctrl, description2, NULL, NULL, pi2);
+      err = agent_askpin (ctrl, description2, NULL, NULL, pi2, NULL);
       if (err == -1)
 	{ /* The re-entered one did not match and the user did not
 	     hit cancel. */

Modified: trunk/agent/command.c
===================================================================
--- trunk/agent/command.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/command.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -169,6 +169,23 @@
 }
 
 
+/* Skip over options.  
+   Blanks after the options are also removed. */
+static char *
+skip_options (const char *line)
+{
+  while (spacep (line))
+    line++;
+  while ( *line == '-' && line[1] == '-' )
+    {
+      while (*line && !spacep (line))
+        line++;
+      while (spacep (line))
+        line++;
+    }
+  return (char*)line;
+}
+
 /* Check whether the option NAME appears in LINE */
 static int
 has_option (const char *line, const char *name)
@@ -177,6 +194,8 @@
   int n = strlen (name);
 
   s = strstr (line, name);
+  if (s && s >= skip_options (line))
+    return 0;
   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
 }
 
@@ -190,6 +209,8 @@
   int n = strlen (name);
 
   s = strstr (line, name);
+  if (s && s >= skip_options (line))
+    return 0;
   return (s && (s == line || spacep (s-1))
           && (!s[n] || spacep (s+n) || s[n] == '='));
 }
@@ -203,6 +224,8 @@
   int n = strlen (name);
 
   s = strstr (line, name);
+  if (s && s >= skip_options (line))
+    return NULL;
   if (s && (s == line || spacep (s-1))
       && s[n] && (spacep (s+n) || s[n] == '='))
     {
@@ -215,23 +238,6 @@
 }
 
 
-/* Skip over options.  It is assumed that leading spaces have been
-   removed (this is the case for lines passed to a handler from
-   assuan).  Blanks after the options are also removed. */
-static char *
-skip_options (char *line)
-{
-  while ( *line == '-' && line[1] == '-' )
-    {
-      while (*line && !spacep (line))
-        line++;
-      while (spacep (line))
-        line++;
-    }
-  return line;
-}
-
-
 /* Replace all '+' by a blank. */
 static void
 plus_to_blank (char *s)
@@ -530,23 +536,35 @@
 
 
 static const char hlp_havekey[] =
-  "HAVEKEY <hexstring_with_keygrip>\n"
+  "HAVEKEY <hexstrings_with_keygrips>\n"
   "\n"
-  "Return success when the secret key is available.";
+  "Return success if at least one of the secret keys with the given\n"
+  "keygrips is available.";
 static gpg_error_t
 cmd_havekey (assuan_context_t ctx, char *line)
 {
-  int rc;
+  gpg_error_t err;
   unsigned char buf[20];
 
-  rc = parse_keygrip (ctx, line, buf);
-  if (rc)
-    return rc;
+  do 
+    {
+      err = parse_keygrip (ctx, line, buf);
+      if (err)
+        return err;
+      
+      if (!agent_key_available (buf))
+        return 0; /* Found.  */
 
-  if (agent_key_available (buf))
-    return gpg_error (GPG_ERR_NO_SECKEY);
-
-  return 0;
+      while (*line && *line != ' ' && *line != '\t')
+        line++;
+      while (*line == ' ' || *line == '\t')
+        line++;
+    }
+  while (*line);
+    
+  /* No leave_cmd() here because errors are expected and would clutter
+     the log.  */
+  return gpg_error (GPG_ERR_NO_SECKEY);
 }
 
 
@@ -1316,9 +1334,14 @@
   ctrl->in_passwd++;
   rc = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc,
                             grip, &shadow_info, CACHE_MODE_IGNORE, NULL, 
-                            &s_skey);
+                            &s_skey, NULL);
   if (rc)
-    ;
+    {
+      /* Not all users of gpg-agent know about fully cancled; thus we
+         map it back.  */
+      if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
+        rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
+    }
   else if (!s_skey)
     {
       log_error ("changing a smartcard PIN is not yet supported\n");
@@ -1590,9 +1613,9 @@
          used to protect the key using the same code as for regular
          key import. */
       
-      err = convert_openpgp (ctrl, openpgp_sexp, grip,
-                             ctrl->server_local->keydesc, cache_nonce,
-                             &key, &passphrase);
+      err = convert_from_openpgp (ctrl, openpgp_sexp, grip,
+                                  ctrl->server_local->keydesc, cache_nonce,
+                                  &key, &passphrase);
       if (err)
         goto leave;
       realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
@@ -1620,7 +1643,7 @@
         err = agent_ask_new_passphrase 
           (ctrl, _("Please enter the passphrase to protect the "
                    "imported object within the GnuPG system."),
-           &passphrase);
+           &passphrase, NULL);
       if (err)
         goto leave;
     }
@@ -1650,7 +1673,7 @@
 
 
 static const char hlp_export_key[] =
-  "EXPORT_KEY <hexstring_with_keygrip>\n"
+  "EXPORT_KEY [--cache-nonce=<nonce>] [--openpgp] <hexstring_with_keygrip>\n"
   "\n"
   "Export a secret key from the key store.  The key will be encrypted\n"
   "using the current session's key wrapping key (cf. command KEYWRAP_KEY)\n"
@@ -1668,6 +1691,26 @@
   gcry_cipher_hd_t cipherhd = NULL;
   unsigned char *wrappedkey = NULL;
   size_t wrappedkeylen;
+  int openpgp;
+  char *cache_nonce;
+  char *passphrase = NULL;
+  
+  openpgp = has_option (line, "--openpgp");
+  cache_nonce = option_value (line, "--cache-nonce");
+  if (cache_nonce)
+    {
+      for (; *line && !spacep (line); line++)
+        ;
+      if (*line)
+        *line++ = '\0';
+      cache_nonce = xtrystrdup (cache_nonce);
+      if (!cache_nonce)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+    }
+  line = skip_options (line);
 
   if (!ctrl->server_local->export_key)
     {
@@ -1685,8 +1728,11 @@
       goto leave;
     }
 
+  /* Get the key from the file.  With the openpgp flag we also ask for
+     the passphrase so that we can use it to re-encrypt it.  */
   err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip,
-                             NULL, CACHE_MODE_IGNORE, NULL, &s_skey);
+                             NULL, CACHE_MODE_IGNORE, NULL, &s_skey,
+                             openpgp ? &passphrase : NULL);
   if (err)
     goto leave;
   if (!s_skey)
@@ -1697,8 +1743,33 @@
       err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
       goto leave;
     }
-
-  err = make_canon_sexp_pad (s_skey, 1, &key, &keylen);
+  
+  if (openpgp)
+    {
+      /* The openpgp option changes the key format into the OpenPGP
+         key transfer format.  The result is already a padded
+         canonical S-expression.  */
+      if (!passphrase)
+        {
+          int fully_canceled;
+          err = agent_ask_new_passphrase 
+            (ctrl, _("This key (or subkey) is not protected with a passphrase."
+                     "  Please enter a new passphrase to export it."),
+             &passphrase, &fully_canceled);
+          if (err)
+            {
+              if (fully_canceled)
+                err = gpg_error (GPG_ERR_FULLY_CANCELED);
+              goto leave;
+            }
+        }
+      err = convert_to_openpgp (ctrl, s_skey, passphrase, &key, &keylen);
+    }
+  else
+    {
+      /* Convert into a canonical S-expression and wrap that.  */
+      err = make_canon_sexp_pad (s_skey, 1, &key, &keylen);
+    }
   if (err)
     goto leave;
   gcry_sexp_release (s_skey);
@@ -1735,12 +1806,18 @@
   
 
  leave:
+  xfree (passphrase);
   xfree (wrappedkey);
   gcry_cipher_close (cipherhd);
   xfree (key);
   gcry_sexp_release (s_skey);
   xfree (ctrl->server_local->keydesc);
   ctrl->server_local->keydesc = NULL;
+
+  /* Not all users of gpg-agent know about fully cancled; thus we map
+     it back unless we know that it is okay.  */
+  if (!openpgp && gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
+    err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
   return leave_cmd (ctx, err);
 }
 

Modified: trunk/agent/cvt-openpgp.c
===================================================================
--- trunk/agent/cvt-openpgp.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/cvt-openpgp.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -525,10 +525,10 @@
    pointed to by GRIP.  On error NULL is stored at all return
    arguments.  */
 gpg_error_t
-convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, 
-                 unsigned char *grip, const char *prompt,
-                 const char *cache_nonce,
-                 unsigned char **r_key, char **r_passphrase)
+convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, 
+                      unsigned char *grip, const char *prompt,
+                      const char *cache_nonce,
+                      unsigned char **r_key, char **r_passphrase)
 {
   gpg_error_t err;
   gcry_sexp_t top_list;
@@ -779,7 +779,7 @@
         err = try_do_unprotect_cb (pi);
     }
   if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
-    err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
+    err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL);
   skeyidx = pi_arg.skeyidx;
   if (!err)
     {
@@ -824,4 +824,267 @@
 }
 
 
+
+static gpg_error_t
+key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array)
+{
+  gpg_error_t err = 0;
+  gcry_sexp_t l2;
+  int idx;
 
+  for (idx=0; *elems; elems++, idx++)
+    {
+      l2 = gcry_sexp_find_token (sexp, elems, 1);
+      if (!l2)
+        {
+          err = gpg_error (GPG_ERR_NO_OBJ); /* Required parameter not found.  */
+          goto leave;
+        }
+      array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+      gcry_sexp_release (l2);
+      if (!array[idx]) 
+        {
+          err = gpg_error (GPG_ERR_INV_OBJ); /* Required parameter invalid.  */
+          goto leave;
+        }
+    }
+  
+ leave:
+  if (err)
+    {
+      int i;
+
+      for (i=0; i < idx; i++)
+        {
+          gcry_mpi_release (array[i]);
+          array[i] = NULL;
+        }
+    }
+  return err;
+}
+
+
+/* Given an ARRAY of mpis with the key parameters, protect the secret
+   parameters in that array and replace them by one opaque encoded
+   mpi.  NPKEY is the number of public key parameters and NSKEY is
+   the number of secret key parameters (including the public ones).
+   On success the array will have NPKEY+1 elements.  */
+static gpg_error_t
+apply_protection (gcry_mpi_t *array, int npkey, int nskey,
+                  const char *passphrase,
+                  int protect_algo, void *protect_iv, size_t protect_ivlen,
+                  int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count)
+{
+  gpg_error_t err;
+  int i, j;
+  gcry_cipher_hd_t cipherhd;
+  unsigned char *bufarr[10];
+  size_t narr[10];
+  unsigned int nbits[10];
+  int ndata;
+  unsigned char *p, *data;
+
+  assert (npkey < nskey);
+  assert (nskey < DIM (bufarr));
+
+  /* Collect only the secret key parameters into BUFARR et al and
+     compute the required size of the data buffer.  */
+  ndata = 20; /* Space for the SHA-1 checksum.  */
+  for (i = npkey, j = 0; i < nskey; i++, j++ )
+    {
+      err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
+      if (err)
+        {
+          err = gpg_error_from_syserror ();
+          for (i = 0; i < j; i++)
+            xfree (bufarr[i]);
+          return err;
+        }
+      nbits[j] = gcry_mpi_get_nbits (array[i]);
+      ndata += 2 + narr[j];
+    }
+
+  /* Allocate data buffer and stuff it with the secret key parameters.  */
+  data = xtrymalloc_secure (ndata);
+  if (!data)
+    {
+      err = gpg_error_from_syserror ();
+      for (i = 0; i < (nskey-npkey); i++ )
+        xfree (bufarr[i]);
+      return err;
+    }
+  p = data;
+  for (i = 0; i < (nskey-npkey); i++ )
+    {
+      *p++ = nbits[i] >> 8 ;
+      *p++ = nbits[i];
+      memcpy (p, bufarr[i], narr[i]);
+      p += narr[i];
+      xfree (bufarr[i]);
+      bufarr[i] = NULL;
+    }
+  assert (p == data + ndata - 20);
+
+  /* Append a hash of the secret key parameters.  */
+  gcry_md_hash_buffer (GCRY_MD_SHA1, p, data, ndata - 20);
+
+  /* Encrypt it.  */
+  err = gcry_cipher_open (&cipherhd, protect_algo,
+                          GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE);
+  if (!err)
+    err = hash_passphrase_and_set_key (passphrase, cipherhd, protect_algo,
+                                       s2k_mode, s2k_algo, s2k_salt, s2k_count);
+  if (!err)
+    err = gcry_cipher_setiv (cipherhd, protect_iv, protect_ivlen);
+  if (!err)
+    err = gcry_cipher_encrypt (cipherhd, data, ndata, NULL, 0);
+  gcry_cipher_close (cipherhd);
+  if (err)
+    {
+      xfree (data);
+      return err;
+    }
+
+  /* Replace the secret key parameters in the array by one opaque value.  */
+  for (i = npkey; i < nskey; i++ )
+    {
+      gcry_mpi_release (array[i]);
+      array[i] = NULL;
+    }
+  array[npkey] = gcry_mpi_set_opaque (NULL, data, ndata*8);
+  return 0;
+}
+
+
+/* Convert our key S_KEY into an OpenPGP key transfer format.  On
+   success a canonical encoded S-expression is stored at R_TRANSFERKEY
+   and its length at R_TRANSFERKEYLEN; this S-expression is also
+   padded to a multiple of 64 bits.  */
+gpg_error_t
+convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
+                    unsigned char **r_transferkey, size_t *r_transferkeylen)
+{
+  gpg_error_t err;
+  gcry_sexp_t list, l2;
+  char *name;
+  int algo;
+  const char *algoname;
+  const char *elems;
+  int npkey, nskey;
+  gcry_mpi_t array[10];
+  char protect_iv[16];
+  char salt[8];
+  unsigned long s2k_count;
+  int i, j;
+
+  (void)ctrl;
+
+  *r_transferkey = NULL;
+
+  for (i=0; i < DIM (array); i++)
+    array[i] = NULL;
+
+  list = gcry_sexp_find_token (s_key, "private-key", 0);
+  if (!list)
+    return gpg_error (GPG_ERR_NO_OBJ); /* Does not contain a key object.  */
+  l2 = gcry_sexp_cadr (list);
+  gcry_sexp_release (list);
+  list = l2;
+  name = gcry_sexp_nth_string (list, 0);
+  if (!name)
+    {
+      gcry_sexp_release (list);
+      return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
+    }
+  
+  algo = gcry_pk_map_name (name);
+  xfree (name);
+
+  switch (algo)
+    {
+    case GCRY_PK_RSA:   algoname = "rsa";   npkey = 2; elems = "nedpqu";  break;
+    case GCRY_PK_ELG:   algoname = "elg";   npkey = 3; elems = "pgyx";    break;
+    case GCRY_PK_ELG_E: algoname = "elg";   npkey = 3; elems = "pgyx";    break;
+    case GCRY_PK_DSA:   algoname = "dsa";   npkey = 4; elems = "pqgyx";   break;
+    case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 6; elems = "pabgnqd"; break;
+    default:            algoname = "";      npkey = 0; elems = NULL;      break;
+    }
+  assert (!elems || strlen (elems) < DIM (array) );
+  nskey = elems? strlen (elems) : 0;
+
+  if (!elems)
+    err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+  else
+    err = key_from_sexp (list, elems, array);
+  gcry_sexp_release (list);
+  if (err)
+    return err;
+
+  gcry_create_nonce (protect_iv, sizeof protect_iv);
+  gcry_create_nonce (salt, sizeof salt);
+  s2k_count = get_standard_s2k_count ();
+  err = apply_protection (array, npkey, nskey, passphrase,
+                          GCRY_CIPHER_AES, protect_iv, sizeof protect_iv,
+                          3, GCRY_MD_SHA1, salt, s2k_count);
+  /* Turn it into the transfer key S-expression.  Note that we always
+     return a protected key.  */
+  if (!err)
+    {
+      char countbuf[35];
+      membuf_t mbuf;
+      void *format_args_buf_ptr[1];
+      int   format_args_buf_int[1];
+      void *format_args[10+2];
+      size_t n;
+      gcry_sexp_t tmpkey, tmpsexp;
+      
+      snprintf (countbuf, sizeof countbuf, "%lu", s2k_count);
+      
+      init_membuf (&mbuf, 50);
+      put_membuf_str (&mbuf, "(skey");
+      for (i=j=0; i < npkey; i++)
+        {
+          put_membuf_str (&mbuf, " _ %m");
+          format_args[j++] = array + i;
+        }
+      put_membuf_str (&mbuf, " e %b");
+      format_args_buf_ptr[0] = gcry_mpi_get_opaque (array[npkey], &n);
+      format_args_buf_int[0] = (n+7)/8;
+      format_args[j++] = format_args_buf_int;
+      format_args[j++] = format_args_buf_ptr;
+      put_membuf_str (&mbuf, ")\n");
+      put_membuf (&mbuf, "", 1);
+
+      tmpkey = NULL;
+      {
+        char *format = get_membuf (&mbuf, NULL);
+        if (!format)
+          err = gpg_error_from_syserror ();
+        else
+          err = gcry_sexp_build_array (&tmpkey, NULL, format, format_args);
+        xfree (format);
+      }
+      if (!err)
+        err = gcry_sexp_build (&tmpsexp, NULL,
+                               "(openpgp-private-key\n"
+                               " (version 1:4)\n"
+                               " (algo %s)\n"
+                               " %S\n"
+                               " (protection sha1 aes %b 1:3 sha1 %b %s))\n",
+                               algoname,
+                               tmpkey, 
+                               (int)sizeof protect_iv, protect_iv,
+                               (int)sizeof salt, salt,
+                               countbuf);
+      gcry_sexp_release (tmpkey);
+      if (!err)
+        err = make_canon_sexp_pad (tmpsexp, 0, r_transferkey, r_transferkeylen);
+      gcry_sexp_release (tmpsexp);
+    }
+
+  for (i=0; i < DIM (array); i++)
+    gcry_mpi_release (array[i]);
+  
+  return err;
+}
+

Modified: trunk/agent/cvt-openpgp.h
===================================================================
--- trunk/agent/cvt-openpgp.h	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/cvt-openpgp.h	2010-10-01 20:33:53 UTC (rev 5433)
@@ -19,10 +19,14 @@
 #ifndef GNUPG_AGENT_CVT_OPENPGP_H
 #define GNUPG_AGENT_CVT_OPENPGP_H
 
-gpg_error_t convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, 
-                             unsigned char *grip, const char *prompt,
-                             const char *cache_nonce,
-                             unsigned char **r_key, char **r_passphrase);
+gpg_error_t convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, 
+                                  unsigned char *grip, const char *prompt,
+                                  const char *cache_nonce,
+                                  unsigned char **r_key, char **r_passphrase);
 
+gpg_error_t convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, 
+                                const char *passphrase,
+                                unsigned char **r_transferkey,
+                                size_t *r_transferkeylen);
 
 #endif /*GNUPG_AGENT_CVT_OPENPGP_H*/

Modified: trunk/agent/divert-scd.c
===================================================================
--- trunk/agent/divert-scd.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/divert-scd.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -266,7 +266,7 @@
 
   if (any_flags)
     {
-      rc = agent_askpin (ctrl, info, prompt, again_text, pi);
+      rc = agent_askpin (ctrl, info, prompt, again_text, pi, NULL);
       again_text = NULL;
       if (!rc && newpin)
         {
@@ -288,7 +288,7 @@
                               is_puk?
                               _("Repeat this PUK"):
                               _("Repeat this PIN")),
-                             prompt, NULL, pi2);
+                             prompt, NULL, pi2, NULL);
           if (!rc && strcmp (pi->pin, pi2->pin))
             {
               again_text = (resetcode? 
@@ -312,7 +312,7 @@
                      info? info:"",
                      info? "')":"") < 0)
         desc = NULL;
-      rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi);
+      rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi, NULL);
       xfree (desc);
     }
 

Modified: trunk/agent/findkey.c
===================================================================
--- trunk/agent/findkey.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/findkey.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -104,7 +104,7 @@
 }
 
 
-/* Callback function to try the unprotection from the passpharse query
+/* Callback function to try the unprotection from the passphrase query
    code. */
 static int
 try_unprotect_cb (struct pin_entry_info_s *pi)
@@ -273,11 +273,16 @@
    should be the hex encoded keygrip of that key to be used with the
    caching mechanism. DESC_TEXT may be set to override the default
    description used for the pinentry.  If LOOKUP_TTL is given this
-   function is used to lookup the default ttl. */
+   function is used to lookup the default ttl.  If R_PASSPHRASE is not
+   NULL, the function succeeded and the key was protected the used
+   passphrase (entered or from the cache) is stored there; if not NULL
+   will be stored.  The caller needs to free the returned
+   passphrase. */
 static int
 unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
            unsigned char **keybuf, const unsigned char *grip, 
-           cache_mode_t cache_mode, lookup_ttl_t lookup_ttl)
+           cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
+           char **r_passphrase)
 {
   struct pin_entry_info_s *pi;
   struct try_unprotect_arg_s arg;
@@ -285,6 +290,10 @@
   unsigned char *result;
   size_t resultlen;
   char hexgrip[40+1];
+  int fully_canceled;
+
+  if (r_passphrase)
+    *r_passphrase = NULL;
   
   bin2hex (grip, 20, hexgrip);
 
@@ -297,13 +306,17 @@
       if (pw)
         {
           rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
-          xfree (pw);
           if (!rc)
             {
+              if (r_passphrase)
+                *r_passphrase = pw;
+              else
+                xfree (pw);
               xfree (*keybuf);
               *keybuf = result;
               return 0;
             }
+          xfree (pw);
         }
     }
 
@@ -318,13 +331,17 @@
       if (pw)
         {
           rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
-          xfree (pw);
           if (!rc)
             {
+              if (r_passphrase)
+                *r_passphrase = pw;
+              else
+                xfree (pw);
               xfree (*keybuf);
               *keybuf = result;
               return 0;
             }
+          xfree (pw);
           rc  = 0;
         }
 
@@ -366,7 +383,9 @@
   arg.change_required = 0;
   pi->check_cb_arg = &arg;
 
-  rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
+  rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi, &fully_canceled);
+  if (gpg_err_code (rc) == GPG_ERR_CANCELED && fully_canceled)
+    rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED);
   if (!rc)
     {
       assert (arg.unprotected_key);
@@ -400,8 +419,13 @@
               return rc;
             }
         }
-      agent_put_cache (hexgrip, cache_mode, pi->pin, 
-                       lookup_ttl? lookup_ttl (hexgrip) : 0);
+      else
+        {
+          agent_put_cache (hexgrip, cache_mode, pi->pin, 
+                           lookup_ttl? lookup_ttl (hexgrip) : 0);
+          if (r_passphrase && *pi->pin)
+            *r_passphrase = xtrystrdup (pi->pin);
+        }
       xfree (*keybuf);
       *keybuf = arg.unprotected_key;
     }
@@ -501,13 +525,17 @@
    not simply pass the TTL value because the value is only needed if
    an unprotect action was needed and looking up the TTL may have some
    overhead (e.g. scanning the sshcontrol file).  If a CACHE_NONCE is
-   given that cache item is first tried to get a passphrase.  */
+   given that cache item is first tried to get a passphrase.  If
+   R_PASSPHRASE is not NULL, the function succeeded and the key was
+   protected the used passphrase (entered or from the cache) is stored
+   there; if not NULL will be stored.  The caller needs to free the
+   returned passphrase.   */
 gpg_error_t
 agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
                      const char *desc_text,
                      const unsigned char *grip, unsigned char **shadow_info,
                      cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
-                     gcry_sexp_t *result)
+                     gcry_sexp_t *result, char **r_passphrase)
 {
   int rc;
   unsigned char *buf;
@@ -518,6 +546,8 @@
   *result = NULL;
   if (shadow_info)
     *shadow_info = NULL;
+  if (r_passphrase)
+    *r_passphrase = NULL;
 
   rc = read_key_file (grip, &s_skey);
   if (rc)
@@ -579,7 +609,7 @@
 	if (!rc)
 	  {
 	    rc = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip,
-                            cache_mode, lookup_ttl);
+                            cache_mode, lookup_ttl, r_passphrase);
 	    if (rc)
 	      log_error ("failed to unprotect the secret key: %s\n",
 			 gpg_strerror (rc));
@@ -626,6 +656,11 @@
   if (rc || got_shadow_info)
     {
       xfree (buf);
+      if (r_passphrase)
+        {
+          xfree (*r_passphrase);
+          *r_passphrase = NULL;
+        }
       return rc;
     }
 
@@ -637,6 +672,11 @@
     {
       log_error ("failed to build S-Exp (off=%u): %s\n",
                  (unsigned int)erroff, gpg_strerror (rc));
+      if (r_passphrase)
+        {
+          xfree (*r_passphrase);
+          *r_passphrase = NULL;
+        }
       return rc;
     }
 

Modified: trunk/agent/genkey.c
===================================================================
--- trunk/agent/genkey.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/genkey.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -290,10 +290,12 @@
    function returns 0 and store the passphrase at R_PASSPHRASE; if the
    user opted not to use a passphrase NULL will be stored there.  The
    user needs to free the returned string.  In case of an error and
-   error code is returned and NULL stored at R_PASSPHRASE.  */
+   error code is returned and NULL stored at R_PASSPHRASE.  If
+   R_CANCEL_ALL is not NULL and the user canceled by directly closing
+   the window true will be stored at this address.  */
 gpg_error_t
 agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
-                          char **r_passphrase)
+                          char **r_passphrase, int *r_cancel_all)
 {
   gpg_error_t err;
   const char *text1 = prompt;
@@ -314,7 +316,7 @@
   pi2->check_cb_arg = pi->pin;
 
  next_try:
-  err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
+  err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi, r_cancel_all);
   initial_errtext = NULL;
   if (!err)
     {
@@ -327,7 +329,7 @@
       /* Unless the passphrase is empty, ask to confirm it.  */
       if (pi->pin && *pi->pin)
         {
-          err = agent_askpin (ctrl, text2, NULL, NULL, pi2);
+          err = agent_askpin (ctrl, text2, NULL, NULL, pi2, NULL);
           if (err == -1)
             { /* The re-entered one did not match and the user did not
                  hit cancel. */
@@ -379,7 +381,7 @@
     rc = agent_ask_new_passphrase (ctrl, 
                                    _("Please enter the passphrase to%0A"
                                      "to protect your new key"),
-                                   &passphrase);
+                                   &passphrase, NULL);
   if (rc)
     return rc;
 
@@ -471,7 +473,7 @@
 
   rc = agent_ask_new_passphrase (ctrl, 
                                  _("Please enter the new passphrase"),
-                                 &passphrase);
+                                 &passphrase, NULL);
   if (!rc)
     {
       rc = store_key (s_skey, passphrase, 1);

Modified: trunk/agent/gpg-agent.c
===================================================================
--- trunk/agent/gpg-agent.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/gpg-agent.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -440,6 +440,11 @@
 static void
 cleanup (void)
 {
+  static int done;
+
+  if (done)
+    return;
+  done = 1;  
   deinitialize_module_cache ();
   remove_socket (socket_name);
   remove_socket (socket_name_ssh);
@@ -724,6 +729,12 @@
               if( parse_debug )
                 log_info (_("NOTE: no default option file `%s'\n"),
                           configname );
+              /* Save the default conf file name so that
+                 reread_configuration is able to test whether the
+                 config file has been created in the meantime.  */
+              xfree (config_filename);
+              config_filename = configname;
+              configname = NULL;
 	    }
           else
             {
@@ -811,10 +822,15 @@
       fclose( configfp );
       configfp = NULL;
       /* Keep a copy of the name so that it can be read on SIGHUP. */
-      config_filename = configname;
+      if (config_filename != configname)
+        {
+          xfree (config_filename);
+          config_filename = configname;
+        }
       configname = NULL;
       goto next_pass;
     }
+    
   xfree (configname);
   configname = NULL;
   if (log_get_errorcount(0))
@@ -1262,6 +1278,12 @@
 agent_exit (int rc)
 {
   /*FIXME: update_random_seed_file();*/
+
+  /* We run our cleanup handler because that may close cipher contexts
+     stored in secure memory and thus this needs to be done before we
+     explicitly terminate secure memory.  */
+  cleanup ();
+
 #if 1
   /* at this time a bit annoying */
   if (opt.debug & DBG_MEMSTAT_VALUE)
@@ -1337,8 +1359,8 @@
   fp = fopen (config_filename, "r");
   if (!fp)
     {
-      log_error (_("option file `%s': %s\n"),
-                 config_filename, strerror(errno) );
+      log_info (_("option file `%s': %s\n"),
+                config_filename, strerror(errno) );
       return;
     }
 

Modified: trunk/agent/keyformat.txt
===================================================================
--- trunk/agent/keyformat.txt	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/keyformat.txt	2010-10-01 20:33:53 UTC (rev 5433)
@@ -14,8 +14,9 @@
 and should have permissions 700.
 
 The secret keys are stored in files with a name matching the
-hexadecimal representation of the keygrip[2].
+hexadecimal representation of the keygrip[2] and suffixed with ".key".
 
+
 Unprotected Private Key Format
 ==============================
 The content of the file is an S-Expression like the ones used with
@@ -166,21 +167,23 @@
 
 (openpgp-private-key
   (version V)
-  (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)
   (algo PUBKEYALGO)
-  (skey CSUM c P1 c P2 c P3 ... e PN))
+  (skey _ P1 _ P2 _ P3 ... e PN)
+  (csum n)
+  (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT))
 
 
 * V is the packet version number (3 or 4).
 * PUBKEYALGO is a Libgcrypt algo name 
-* CSUM is the 16 bit checksum as defined by OpenPGP.
 * P1 .. PN are the parameters; the public parameters are never encrypted
   the secrect key parameters are encrypted if the "protection" list is
   given.  To make this more explicit each parameter is preceded by a
   flag "_" for cleartext or "e" for encrypted text.
+* CSUM is the depreciated 16 bit checksum as defined by OpenPGP.  This
+  is an optional element.
 * If PROTTYPE is "sha1" the new style SHA1 checksum is used if it is "sum"
-  the old 16 bit checksum is used and if it is "none" no protection at
-  all is used.
+  the old 16 bit checksum (above) is used and if it is "none" no
+  protection at all is used.
 * PROTALGO is a Libgcrypt style cipher algorithm name
 * IV is the initialization verctor.
 * S2KMODE is the value from RFC-4880.
@@ -189,7 +192,106 @@
 * S2KCOUNT is the count value from RFC-4880.
 
 
+Persistent Passphrase Format
+============================
 
+To allow persistent storage of cached passphrases we use a scheme
+similar to the private-key storage format.  This is a master
+passphrase format where each file may protect several secrets under
+one master passphrase.  It is possible to have several of those files
+each protected by a dedicated master passphrase.  Clear text keywords
+allow to list the available protected passphrases.
+
+The name of the files with these protected secrets have this form:
+pw-<string>.dat.  STRING may be an arbitrary string, as a default name
+for the passphrase storage the name "pw-default.dat" is suggested.
+
+
+(protected-shared-secret
+   ((desc descriptive_text)
+    (key [key_1] (keyword_1 keyword_2 keyword_n))
+    (key [key_2] (keyword_21 keyword_22 keyword_2n))
+    (key [key_n] (keyword_n1 keyword_n2 keyword_nn))
+    (protected mode (parms) encrypted_octet_string)
+    (protected-at <isotimestamp>)
+   )
+)  
+
+After decryption the encrypted_octet_string yields this S-expression:
+
+(
+ (
+  (value key_1 value_1)
+  (value key_2 value_2)
+  (value key_n value_n)
+ ) 
+ (hash sha1 #...[hashvalue]...#)
+)
+
+The "descriptive_text" is displayed with the prompt to enter the
+unprotection passphrase.
+
+KEY_1 to KEY_N are unique identifiers for the shared secret, for
+example an URI.  In case this information should be kept confidential
+as well, they may not appear in the unprotected part; however they are
+mandatory in the encrypted_octet_string.  The list of keywords is
+optional.  The oder of the "key" lists and the order of the "value"
+lists mut match, that is the first "key"-list is associated with the
+first "value" list in the encrypted_octet_string.
+
+The protection mode etc. is indentical to the protection mode as
+decribed for the private key format.
+
+list of the secret key parameters.  The protected-at expression is
+optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").
+
+The "hash" in the encrypted_octet_string is calculated on the
+concatenation of the key list and value lists: i.e it is required to
+hash the concatenation of all these lists, including the
+parenthesis and (if used) the protected-at list.
+
+Example:
+
+(protected-shared-secret
+   ((desc "List of system passphrases")
+    (key "uid-1002" ("Knuth" "Donald Ervin Knuth"))
+    (key "uid-1001" ("Dijkstra" "Edsgar Wybe Dijkstra"))
+    (key)
+    (protected mode (parms) encrypted_octet_string)
+    (protected-at "20100915T111722")
+   )
+)  
+
+with "encrypted_octet_string" decoding to:
+
+(
+ (
+  (value 4:1002 "signal flags at the lock")
+  (value 4:1001 "taocp")
+  (value 1:0    "premature optimization is the root of all evil")
+ ) 
+ (hash sha1 #0102030405060708091011121314151617181920#)
+)
+
+To compute the hash this S-expression (in canoncical format) was
+hashed:
+
+   ((desc "List of system passphrases")
+    (key "uid-1002" ("Knuth" "Donald Ervin Knuth"))
+    (key "uid-1001" ("Dijkstra" "Edsgar Wybe Dijkstra"))
+    (key)
+    (value 4:1002 "signal flags at the lock")
+    (value 4:1001 "taocp")
+    (value 1:0    "premature optimization is the root of all evil")
+    (protected-at "20100915T111722")
+   )
+
+
+
+
+
+
+
 Notes:
 ======
 [1] I usually use the terms private and secret key exchangeable but prefer the

Modified: trunk/agent/pkdecrypt.c
===================================================================
--- trunk/agent/pkdecrypt.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/pkdecrypt.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -66,7 +66,9 @@
     }
   rc = agent_key_from_file (ctrl, NULL, desc_text,
                             ctrl->keygrip, &shadow_info,
-                            CACHE_MODE_NORMAL, NULL, &s_skey);
+                            CACHE_MODE_NORMAL, NULL, &s_skey, NULL);
+  if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
+    rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
   if (rc)
     {
       if (gpg_err_code (rc) == GPG_ERR_ENOENT)

Modified: trunk/agent/pksign.c
===================================================================
--- trunk/agent/pksign.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/pksign.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -255,7 +255,9 @@
 
   rc = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip,
                             &shadow_info, cache_mode, lookup_ttl,
-                            &s_skey);
+                            &s_skey, NULL);
+  if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
+    rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
   if (rc)
     {
       log_error ("failed to read the secret key\n");

Modified: trunk/agent/protect.c
===================================================================
--- trunk/agent/protect.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/protect.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -191,14 +191,16 @@
 
 
 
-/* Calculate the MIC for a private key S-Exp. SHA1HASH should point to
-   a 20 byte buffer.  This function is suitable for any algorithms. */
+/* Calculate the MIC for a private key or shared secret S-expression.
+   SHA1HASH should point to a 20 byte buffer.  This function is
+   suitable for all algorithms. */
 static int 
 calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
 {
   const unsigned char *hash_begin, *hash_end;
   const unsigned char *s;
   size_t n;
+  int is_shared_secret;
 
   s = plainkey;
   if (*s != '(')
@@ -207,16 +209,23 @@
   n = snext (&s);
   if (!n)
     return gpg_error (GPG_ERR_INV_SEXP); 
-  if (!smatch (&s, n, "private-key"))
+  if (smatch (&s, n, "private-key"))
+    is_shared_secret = 0;
+  else if (smatch (&s, n, "shared-secret"))
+    is_shared_secret = 1;
+  else
     return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
   if (*s != '(')
     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
   hash_begin = s;
-  s++;
-  n = snext (&s);
-  if (!n)
-    return gpg_error (GPG_ERR_INV_SEXP); 
-  s += n; /* skip over the algorithm name */
+  if (!is_shared_secret)
+    {
+      s++;
+      n = snext (&s);
+      if (!n)
+        return gpg_error (GPG_ERR_INV_SEXP); 
+      s += n; /* Skip the algorithm name.  */
+    }
 
   while (*s == '(')
     {
@@ -955,7 +964,7 @@
       xfree (final);
       return rc;
     }
-  /* Now remove tha part which is included in the MIC but should not
+  /* Now remove the part which is included in the MIC but should not
      go into the final thing.  */
   if (cutlen)
     {

Modified: trunk/agent/t-protect.c
===================================================================
--- trunk/agent/t-protect.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/agent/t-protect.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -289,8 +289,15 @@
 }
 
 
+static void
+test_agent_protect_shared_secret (void)
+{
 
+}
 
+
+
+
 int
 main (int argc, char **argv)
 {
@@ -305,6 +312,7 @@
   test_make_shadow_info ();
   test_agent_shadow_key ();
   test_agent_get_shadow_info ();
+  test_agent_protect_shared_secret ();
 
   return 0;
 }

Modified: trunk/common/http.c
===================================================================
--- trunk/common/http.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/common/http.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -114,6 +114,9 @@
 #ifndef EAGAIN
 #define EAGAIN  EWOULDBLOCK
 #endif
+#ifndef INADDR_NONE  /* Slowaris is missing that.  */
+#define INADDR_NONE  ((unsigned long)(-1))
+#endif /*INADDR_NONE*/
 
 #define HTTP_PROXY_ENV           "http_proxy"
 #define MAX_LINELEN 20000  /* Max. length of a HTTP header line. */

Modified: trunk/common/logging.c
===================================================================
--- trunk/common/logging.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/common/logging.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -68,6 +68,9 @@
 #ifndef EAFNOSUPPORT
 # define EAFNOSUPPORT EINVAL
 #endif
+#ifndef INADDR_NONE  /* Slowaris is missing that.  */
+#define INADDR_NONE  ((unsigned long)(-1))
+#endif /*INADDR_NONE*/
 
 #ifdef HAVE_W32_SYSTEM
 #define sock_close(a)  closesocket(a)

Modified: trunk/common/sexp-parse.h
===================================================================
--- trunk/common/sexp-parse.h	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/common/sexp-parse.h	2010-10-01 20:33:53 UTC (rev 5433)
@@ -82,7 +82,7 @@
 
 /* Check whether the the string at the address BUF points to matches
    the token.  Return true on match and update BUF to point behind the
-   token.  Return false and dont update tha buffer if it does not
+   token.  Return false and do not update the buffer if it does not
    match. */
 static inline int
 smatch (unsigned char const **buf, size_t buflen, const char *token)

Modified: trunk/common/status.c
===================================================================
--- trunk/common/status.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/common/status.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -58,6 +58,7 @@
 
     case GPG_ERR_NOT_TRUSTED:     errstr = "10"; break;
     case GPG_ERR_MISSING_CERT:    errstr = "11"; break;
+    case GPG_ERR_MISSING_ISSUER_CERT: errstr = "12"; break;
     default:                      errstr = "0"; break;
     }
 

Modified: trunk/common/util.h
===================================================================
--- trunk/common/util.h	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/common/util.h	2010-10-01 20:33:53 UTC (rev 5433)
@@ -33,6 +33,12 @@
 #ifndef GPG_ERR_NOT_INITIALIZED
 #define GPG_ERR_NOT_INITIALIZED 184
 #endif
+#ifndef GPG_ERR_MISSING_ISSUER_CERT
+#define GPG_ERR_MISSING_ISSUER_CERT 185
+#endif
+#ifndef GPG_ERR_FULLY_CANCELED
+#define GPG_ERR_FULLY_CANCELED 198
+#endif
 
 
 /* Hash function used with libksba. */

Modified: trunk/configure.ac
===================================================================
--- trunk/configure.ac	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/configure.ac	2010-10-01 20:33:53 UTC (rev 5433)
@@ -396,7 +396,7 @@
 #  define GNUPG_DEFAULT_HOMEDIR "/gnupg"
 # endif
 #elif defined(__VMS)
-#define GNUPG_DEFAULT_HOMEDIR "/SYS\$LOGIN/gnupg" 
+#define GNUPG_DEFAULT_HOMEDIR "/SYS$LOGIN/gnupg" 
 #else
 #define GNUPG_DEFAULT_HOMEDIR "~/.gnupg"
 #endif 

Modified: trunk/dirmngr/validate.c
===================================================================
--- trunk/dirmngr/validate.c	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/dirmngr/validate.c	2010-10-01 20:33:53 UTC (rev 5433)
@@ -623,7 +623,7 @@
             log_error (_("issuer certificate not found: %s\n"),
                          gpg_strerror (err));
           /* Use a better understandable error code.  */
-          err = gpg_error (GPG_ERR_MISSING_CERT);
+          err = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
           goto leave;
         }
 

Modified: trunk/doc/DETAILS
===================================================================
--- trunk/doc/DETAILS	2010-09-29 18:42:43 UTC (rev 5432)
+++ trunk/doc/DETAILS	2010-10-01 20:33:53 UTC (rev 5433)
@@ -586,7 +586,8 @@
           8 := "Policy mismatch"
           9 := "Not a secret key"
 	 10 := "Key not trusted"
-         11 := "Missing certificate"  (e.g. intermediate or root cert.)
+         11 := "Missing certificate" 
+         12 := "Missing issuer certificate"
 
         Note that for historical reasons the INV_RECP status is also
         used for gpgsm's SIGNER command where it relates to signer's





More information about the Gnupg-commits mailing list