s/AES/AES128/ in diagnostics and --list-config
[gnupg.git] / agent / command.c
index 9bd5ce5..8ae313e 100644 (file)
@@ -1,6 +1,6 @@
 /* command.c - gpg-agent command handler
  * Copyright (C) 2001, 2002, 2003, 2004, 2005,
- *               2006, 2008, 2009  Free Software Foundation, Inc.
+ *               2006, 2008, 2009, 2010  Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -37,6 +37,8 @@
 #include "agent.h"
 #include <assuan.h>
 #include "i18n.h"
+#include "cvt-openpgp.h"
+
 
 /* Maximum allowed size of the inquired ciphertext.  */
 #define MAXLEN_CIPHERTEXT 4096
@@ -69,6 +71,9 @@ struct server_local_s
                                 be done. */
   void *import_key;  /* Malloced KEK for the import_key command.  */
   void *export_key;  /* Malloced KEK for the export_key command.  */
+  int allow_fully_canceled; /* Client is aware of GPG_ERR_FULLY_CANCELED.  */
+  char *last_cache_nonce;   /* Last CACHE_NOCNE sent as status (malloced).  */
+  char *last_passwd_nonce;  /* Last PASSWD_NOCNE sent as status (malloced). */
 };
 
 
@@ -150,6 +155,26 @@ write_and_clear_outbuf (assuan_context_t ctx, membuf_t *mb)
 }
 
 
+static void
+clear_nonce_cache (ctrl_t ctrl)
+{
+  if (ctrl->server_local->last_cache_nonce)
+    {
+      agent_put_cache (ctrl->server_local->last_cache_nonce,
+                       CACHE_MODE_NONCE, NULL, 0);
+      xfree (ctrl->server_local->last_cache_nonce);
+      ctrl->server_local->last_cache_nonce = NULL;
+    }
+  if (ctrl->server_local->last_passwd_nonce)
+    {
+      agent_put_cache (ctrl->server_local->last_passwd_nonce,
+                       CACHE_MODE_NONCE, NULL, 0);
+      xfree (ctrl->server_local->last_passwd_nonce);
+      ctrl->server_local->last_passwd_nonce = NULL;
+    }
+}
+
+
 static gpg_error_t
 reset_notify (assuan_context_t ctx, char *line)
 {
@@ -163,10 +188,30 @@ reset_notify (assuan_context_t ctx, char *line)
 
   xfree (ctrl->server_local->keydesc);
   ctrl->server_local->keydesc = NULL;
+
+  clear_nonce_cache (ctrl);
+
   return 0;
 }
 
 
+/* 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)
@@ -175,6 +220,8 @@ has_option (const char *line, const char *name)
   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)));
 }
 
@@ -188,6 +235,8 @@ has_option_name (const char *line, const char *name)
   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] == '='));
 }
@@ -201,6 +250,8 @@ option_value (const char *line, const char *name)
   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] == '='))
     {
@@ -213,23 +264,6 @@ option_value (const char *line, const char *name)
 }
 
 
-/* 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)
@@ -355,6 +389,22 @@ leave_cmd (assuan_context_t ctx, gpg_error_t err)
       const char *name = assuan_get_command_name (ctx);
       if (!name)
         name = "?";
+
+      /* Not all users of gpg-agent know about the fully canceled
+         error code; map it back if needed.  */
+      if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
+        {
+          ctrl_t ctrl = assuan_get_pointer (ctx);
+
+          if (!ctrl->server_local->allow_fully_canceled)
+            err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED);
+        }
+
+      /* Most code from common/ does not know the error source, thus
+         we fix this here.  */
+      if (gpg_err_source (err) == GPG_ERR_SOURCE_UNKNOWN)
+        err = gpg_err_make (GPG_ERR_SOURCE_DEFAULT, gpg_err_code (err));
+
       if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
         log_error ("command '%s' failed: %s\n", name,
                    gpg_strerror (err));
@@ -522,23 +572,35 @@ cmd_marktrusted (assuan_context_t ctx, char *line)
 
 \f
 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;
-
-  if (agent_key_available (buf))
-    return gpg_error (GPG_ERR_NO_SECKEY);
+  do 
+    {
+      err = parse_keygrip (ctx, line, buf);
+      if (err)
+        return err;
+      
+      if (!agent_key_available (buf))
+        return 0; /* Found.  */
 
-  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);
 }
 
 
@@ -564,8 +626,8 @@ cmd_sigkey (assuan_context_t ctx, char *line)
 static const char hlp_setkeydesc[] = 
   "SETKEYDESC plus_percent_escaped_string\n"
   "\n"
-  "Set a description to be used for the next PKSIGN or PKDECRYPT\n"
-  "operation if this operation requires the entry of a passphrase.  If\n"
+  "Set a description to be used for the next PKSIGN, PKDECRYPT, IMPORT_KEY\n"
+  "or EXPORT_KEY operation if this operation requires a passphrase.  If\n"
   "this command is not used a default text will be used.  Note, that\n"
   "this description implictly selects the label used for the entry\n"
   "box; if the string contains the string PIN (which in general will\n"
@@ -573,8 +635,8 @@ static const char hlp_setkeydesc[] =
   "\"passphrase\" is used.  The description string should not contain\n"
   "blanks unless they are percent or '+' escaped.\n"
   "\n"
-  "The description is only valid for the next PKSIGN or PKDECRYPT\n"
-  "operation.";
+  "The description is only valid for the next PKSIGN, PKDECRYPT,\n"
+  "IMPORT_KEY or EXPORT_KEY operation.";
 static gpg_error_t
 cmd_setkeydesc (assuan_context_t ctx, char *line)
 {
@@ -687,7 +749,7 @@ cmd_sethash (assuan_context_t ctx, char *line)
 
 
 static const char hlp_pksign[] = 
-  "PKSIGN [options]\n"
+  "PKSIGN [<options>] [<cache_nonce>]\n"
   "\n"
   "Perform the actual sign operation.  Neither input nor output are\n"
   "sensitive to eavesdropping.";
@@ -698,9 +760,18 @@ cmd_pksign (assuan_context_t ctx, char *line)
   cache_mode_t cache_mode = CACHE_MODE_NORMAL;
   ctrl_t ctrl = assuan_get_pointer (ctx);
   membuf_t outbuf;
+  char *cache_nonce = NULL;
+  char *p;
   
-  (void)line;
+  line = skip_options (line);
   
+  p = line;
+  for (p=line; *p && *p != ' ' && *p != '\t'; p++)
+    ;
+  *p = '\0';
+  if (*line)
+    cache_nonce = xtrystrdup (line);
+
   if (opt.ignore_cache_for_signing)
     cache_mode = CACHE_MODE_IGNORE;
   else if (!ctrl->server_local->use_cache_for_signing)
@@ -708,12 +779,14 @@ cmd_pksign (assuan_context_t ctx, char *line)
 
   init_membuf (&outbuf, 512);
 
-  rc = agent_pksign (ctrl, ctrl->server_local->keydesc,
+  rc = agent_pksign (ctrl, cache_nonce, ctrl->server_local->keydesc,
                      &outbuf, cache_mode);
   if (rc)
     clear_outbuf (&outbuf);
   else
     rc = write_and_clear_outbuf (ctx, &outbuf);
+
+  xfree (cache_nonce);
   xfree (ctrl->server_local->keydesc);
   ctrl->server_local->keydesc = NULL;
   return leave_cmd (ctx, rc);
@@ -721,7 +794,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
 
 
 static const char hlp_pkdecrypt[] = 
-  "PKDECRYPT <options>\n"
+  "PKDECRYPT [<options>]\n"
   "\n"
   "Perform the actual decrypt operation.  Input is not\n"
   "sensitive to eavesdropping.";
@@ -758,7 +831,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
 
 
 static const char hlp_genkey[] = 
-  "GENKEY\n"
+  "GENKEY [--no-protection] [<cache_nonce>]\n"
   "\n"
   "Generate a new key, store the secret part and return the public\n"
   "part.  Here is an example transaction:\n"
@@ -776,11 +849,22 @@ cmd_genkey (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
+  int no_protection;
   unsigned char *value;
   size_t valuelen;
   membuf_t outbuf;
+  char *cache_nonce = NULL;
+  char *p;
+  
+  no_protection = has_option (line, "--no-protection");
+  line = skip_options (line);
 
-  (void)line;
+  p = line;
+  for (p=line; *p && *p != ' ' && *p != '\t'; p++)
+    ;
+  *p = '\0';
+  if (*line)
+    cache_nonce = xtrystrdup (line);
 
   /* First inquire the parameters */
   rc = assuan_inquire (ctx, "KEYPARAM", &value, &valuelen, MAXLEN_KEYPARAM);
@@ -789,12 +873,14 @@ cmd_genkey (assuan_context_t ctx, char *line)
 
   init_membuf (&outbuf, 512);
 
-  rc = agent_genkey (ctrl, (char*)value, valuelen, &outbuf);
+  rc = agent_genkey (ctrl, cache_nonce, (char*)value, valuelen, no_protection,
+                     &outbuf);
   xfree (value);
   if (rc)
     clear_outbuf (&outbuf);
   else
     rc = write_and_clear_outbuf (ctx, &outbuf);
+  xfree (cache_nonce);
   return leave_cmd (ctx, rc);
 }
 
@@ -1042,12 +1128,11 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
-  const char *pw;
+  char *pw;
   char *response;
   char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL;
   const char *desc2 = _("Please re-enter this passphrase");
   char *p;
-  void *cache_marker;
   int opt_data, opt_check, opt_no_ask, opt_qualbar;
   int opt_repeat = 0;
   char *repeat_errtext = NULL;
@@ -1108,12 +1193,11 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
   if (!strcmp (desc, "X"))
     desc = NULL;
 
-  pw = cacheid ? agent_get_cache (cacheid, CACHE_MODE_NORMAL, &cache_marker)
-               : NULL;
+  pw = cacheid ? agent_get_cache (cacheid, CACHE_MODE_NORMAL) : NULL;
   if (pw)
     {
       rc = send_back_passphrase (ctx, opt_data, pw);
-      agent_unlock_cache_entry (&cache_marker);
+      xfree (pw);
     }
   else if (opt_no_ask)
     rc = gpg_error (GPG_ERR_NO_DATA);
@@ -1272,44 +1356,135 @@ cmd_learn (assuan_context_t ctx, char *line)
 
 \f
 static const char hlp_passwd[] = 
-  "PASSWD <hexstring_with_keygrip>\n"
+  "PASSWD [--cache-nonce=<c>] [--passwd-nonce=<s>] <hexstring_with_keygrip>\n"
   "\n"
   "Change the passphrase/PIN for the key identified by keygrip in LINE.";
 static gpg_error_t
 cmd_passwd (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
-  int rc;
+  gpg_error_t err;
+  int c;
+  char *cache_nonce = NULL;
+  char *passwd_nonce = NULL;
   unsigned char grip[20];
   gcry_sexp_t s_skey = NULL;
   unsigned char *shadow_info = NULL;
+  char *passphrase = NULL;
+  char *pend;
 
-  rc = parse_keygrip (ctx, line, grip);
-  if (rc)
+  cache_nonce = option_value (line, "--cache-nonce");
+  if (cache_nonce)
+    {
+      for (pend = cache_nonce; *pend && !spacep (pend); pend++)
+        ;
+      c = *pend;
+      *pend = '\0';
+      cache_nonce = xtrystrdup (cache_nonce);
+      *pend = c;
+      if (!cache_nonce)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+    }
+
+  passwd_nonce = option_value (line, "--passwd-nonce");
+  if (passwd_nonce)
+    {
+      for (pend = passwd_nonce; *pend && !spacep (pend); pend++)
+        ;
+      c = *pend;
+      *pend = '\0';
+      passwd_nonce = xtrystrdup (passwd_nonce);
+      *pend = c;
+      if (!passwd_nonce)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+    }
+
+  line = skip_options (line);
+
+  err = parse_keygrip (ctx, line, grip);
+  if (err)
     goto leave;
 
   ctrl->in_passwd++;
-  rc = agent_key_from_file (ctrl, ctrl->server_local->keydesc,
-                            grip, &shadow_info, CACHE_MODE_IGNORE, NULL, 
-                            &s_skey);
-  if (rc)
+  err = agent_key_from_file (ctrl, cache_nonce, ctrl->server_local->keydesc,
+                             grip, &shadow_info, CACHE_MODE_IGNORE, NULL, 
+                             &s_skey, &passphrase);
+  if (err)
     ;
   else if (!s_skey)
     {
       log_error ("changing a smartcard PIN is not yet supported\n");
-      rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
     }
   else
-    rc = agent_protect_and_store (ctrl, s_skey);
+    {
+      char *newpass = NULL;
+
+      if (passwd_nonce)
+        newpass = agent_get_cache (passwd_nonce, CACHE_MODE_NONCE);
+      err = agent_protect_and_store (ctrl, s_skey, &newpass);
+      if (!err && passphrase)
+        {
+          /* A passphrase existed on the old key and the change was
+             successful.  Return a nonce for that old passphrase to
+             let the caller try to unprotect the other subkeys with
+             the same key.  */
+          if (!cache_nonce)
+            {
+              char buf[12];
+              gcry_create_nonce (buf, 12);
+              cache_nonce = bin2hex (buf, 12, NULL);
+            }
+          if (cache_nonce 
+              && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE,
+                                   passphrase, 120 /*seconds*/))
+            {
+              assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
+              xfree (ctrl->server_local->last_cache_nonce);
+              ctrl->server_local->last_cache_nonce = cache_nonce;
+              cache_nonce = NULL;
+            }
+          if (newpass)
+            {
+              /* If we have a new passphrase (which might be empty) we
+                 store it under a passwd nonce so that the caller may
+                 send that nonce again to use it for another key. */
+              if (!passwd_nonce)
+                {
+                  char buf[12];
+                  gcry_create_nonce (buf, 12);
+                  passwd_nonce = bin2hex (buf, 12, NULL);
+                }
+              if (passwd_nonce 
+                  && !agent_put_cache (passwd_nonce, CACHE_MODE_NONCE,
+                                       newpass, 120 /*seconds*/))
+                {
+                  assuan_write_status (ctx, "PASSWD_NONCE", passwd_nonce);
+                  xfree (ctrl->server_local->last_passwd_nonce);
+                  ctrl->server_local->last_passwd_nonce = passwd_nonce;
+                  passwd_nonce = NULL;
+                }
+            }
+        }
+      xfree (newpass);
+    }
   ctrl->in_passwd--;
 
   xfree (ctrl->server_local->keydesc);
   ctrl->server_local->keydesc = NULL;
 
  leave:
+  xfree (passphrase);
   gcry_sexp_release (s_skey);
   xfree (shadow_info);
-  return leave_cmd (ctx, rc);
+  xfree (cache_nonce);
+  return leave_cmd (ctx, err);
 }
 
 
@@ -1455,7 +1630,7 @@ cmd_keywrap_key (assuan_context_t ctx, char *line)
 
 \f
 static const char hlp_import_key[] =
-  "IMPORT_KEY\n"
+  "IMPORT_KEY [<cache_nonce>]\n"
   "\n"
   "Import a secret key into the key store.  The key is expected to be\n"
   "encrypted using the current session's key wrapping key (cf. command\n"
@@ -1476,15 +1651,23 @@ cmd_import_key (assuan_context_t ctx, char *line)
   unsigned char *finalkey = NULL;
   size_t finalkeylen;
   unsigned char grip[20];
+  gcry_sexp_t openpgp_sexp = NULL;
+  char *cache_nonce = NULL;
+  char *p;
   
-  (void)line;
-
   if (!ctrl->server_local->import_key)
     {
-      err = gpg_error (GPG_ERR_BAD_KEY);
+      err = gpg_error (GPG_ERR_MISSING_KEY);
       goto leave;
     }
 
+  p = line;
+  for (p=line; *p && *p != ' ' && *p != '\t'; p++)
+    ;
+  *p = '\0';
+  if (*line)
+    cache_nonce = xtrystrdup (line);
+
   assuan_begin_confidential (ctx);
   err = assuan_inquire (ctx, "KEYDATA",
                         &wrappedkey, &wrappedkeylen, MAXLEN_KEYDATA);
@@ -1526,20 +1709,71 @@ cmd_import_key (assuan_context_t ctx, char *line)
   
   err = keygrip_from_canon_sexp (key, realkeylen, grip);
   if (err)
-    goto leave;
-
-  if (!agent_key_available (grip))
     {
-      err = gpg_error (GPG_ERR_EEXIST);
-      goto leave;
+      /* This might be due to an unsupported S-expression format.
+         Check whether this is openpgp-private-key and trigger that
+         import code.  */
+      if (!gcry_sexp_sscan (&openpgp_sexp, NULL, key, realkeylen))
+        {
+          const char *tag;
+          size_t taglen;
+          
+          tag = gcry_sexp_nth_data (openpgp_sexp, 0, &taglen);
+          if (tag && taglen == 19 && !memcmp (tag, "openpgp-private-key", 19))
+            ;
+          else
+            {
+              gcry_sexp_release (openpgp_sexp);
+              openpgp_sexp = NULL;
+            }
+        }
+      if (!openpgp_sexp)
+        goto leave; /* Note that ERR is still set.  */
     }
 
-  err = agent_ask_new_passphrase 
-    (ctrl, _("Please enter the passphrase to protect the "
-             "imported object within the GnuPG system."),
-     &passphrase);
-  if (err)
-    goto leave;
+
+  if (openpgp_sexp)
+    {
+      /* In most cases the key is encrypted and thus the conversion
+         function from the OpenPGP format to our internal format will
+         ask for a passphrase.  That passphrase will be returned and
+         used to protect the key using the same code as for regular
+         key import. */
+      
+      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);
+      if (!realkeylen)
+        goto leave; /* Invalid canonical encoded S-expression.  */
+      if (passphrase)
+        {
+          if (!cache_nonce)
+            {
+              char buf[12];
+              gcry_create_nonce (buf, 12);
+              cache_nonce = bin2hex (buf, 12, NULL);
+            }
+          if (cache_nonce 
+              && !agent_put_cache (cache_nonce, CACHE_MODE_NONCE,
+                                   passphrase, 120 /*seconds*/))
+            assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
+        }
+    }
+  else
+    {
+      if (!agent_key_available (grip))
+        err = gpg_error (GPG_ERR_EEXIST);
+      else
+        err = agent_ask_new_passphrase 
+          (ctrl, _("Please enter the passphrase to protect the "
+                   "imported object within the GnuPG system."),
+           &passphrase);
+      if (err)
+        goto leave;
+    }
 
   if (passphrase)
     {
@@ -1551,26 +1785,158 @@ cmd_import_key (assuan_context_t ctx, char *line)
     err = agent_write_private_key (grip, key, realkeylen, 0);
 
  leave:
+  gcry_sexp_release (openpgp_sexp);
   xfree (finalkey);
   xfree (passphrase);
   xfree (key);
   gcry_cipher_close (cipherhd);
   xfree (wrappedkey);
+  xfree (cache_nonce);
+  xfree (ctrl->server_local->keydesc);
+  ctrl->server_local->keydesc = NULL;
   return leave_cmd (ctx, err);
 }
 
 
 \f
 static const char hlp_export_key[] =
-  "EXPORT_KEY\n"
-  "\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"
+  "using the AESWRAP-128 algorithm.  The caller needs to retrieve that key\n"
+  "prior to using this command.  The function takes the keygrip as argument.\n";
 static gpg_error_t
 cmd_export_key (assuan_context_t ctx, char *line)
 {
-  gpg_error_t err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  unsigned char grip[20];
+  gcry_sexp_t s_skey = NULL;
+  unsigned char *key = NULL;
+  size_t keylen;
+  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)
+    {
+      err = gpg_error (GPG_ERR_MISSING_KEY);
+      goto leave;
+    }
+
+  err = parse_keygrip (ctx, line, grip);
+  if (err)
+    goto leave;
+
+  if (agent_key_available (grip))
+    {
+      err = gpg_error (GPG_ERR_NO_SECKEY);
+      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,
+                             openpgp ? &passphrase : NULL);
+  if (err)
+    goto leave;
+  if (!s_skey)
+    {
+      /* Key is on a smartcard.  Actually we should not see this here
+         because we do not pass a shadow_info variable to the above
+         function, thus it will return this error directly.  */
+      err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
+      goto leave;
+    }
+  
+  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)
+        {
+          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);
+          if (err)
+            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);
+  s_skey = NULL;
+
+  err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
+                          GCRY_CIPHER_MODE_AESWRAP, 0);
+  if (err)
+    goto leave;
+  err = gcry_cipher_setkey (cipherhd,
+                            ctrl->server_local->export_key, KEYWRAP_KEYSIZE);
+  if (err)
+    goto leave;
 
+  wrappedkeylen = keylen + 8;
+  wrappedkey = xtrymalloc (wrappedkeylen);
+  if (!wrappedkey)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen);
+  if (err)
+    goto leave;
+  xfree (key);
+  key = NULL;
+  gcry_cipher_close (cipherhd);
+  cipherhd = NULL;
+
+  assuan_begin_confidential (ctx);
+  err = assuan_send_data (ctx, wrappedkey, wrappedkeylen);
+  assuan_end_confidential (ctx);
+  
+
+ leave:
+  xfree (cache_nonce);
+  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;
 
- /* leave: */
   return leave_cmd (ctx, err);
 }
 
@@ -1938,7 +2304,14 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err = 0;
 
-  if (!strcmp (key, "putenv"))
+  if (!strcmp (key, "agent-awareness"))
+    {
+      /* The value is a version string telling us of which agent
+         version the caller is aware of.  */
+      ctrl->server_local->allow_fully_canceled = 
+        gnupg_compare_version (value, "2.1.0");
+    }
+  else if (!strcmp (key, "putenv"))
     {
       /* Change the session's environment to be used for the
          Pinentry.  Valid values are:
@@ -2192,6 +2565,9 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd)
         }
     }
 
+  /* Reset the nonce caches.  */
+  clear_nonce_cache (ctrl);
+
   /* Reset the SCD if needed. */
   agent_reset_scd (ctrl);