[Pkg-gnupg-commit] [gnupg2] 02/04: more patches from upstream
Daniel Kahn Gillmor
dkg at fifthhorseman.net
Wed Oct 26 01:41:52 UTC 2016
This is an automated email from the git hooks/post-receive script.
dkg pushed a commit to branch master
in repository gnupg2.
commit 1c5cb4e684fa9194d6d378604d6023797f9aff59
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date: Mon Oct 17 16:38:01 2016 -0400
more patches from upstream
---
debian/patches/0064-tools-Fix-error-handling.patch | 34 +
.../0065-g10-Fix-a-column-s-type-in-TOFU-DB.patch | 29 +
...ove-inotify-code-to-common-and-improve-it.patch | 335 +++++++
...traightforward-names-for-the-default-sock.patch | 35 +
debian/patches/0068-gpgconf-Fix-for-homedir.patch | 158 ++++
.../patches/0069-scd-Fix-keytocard-for-ECC.patch | 28 +
...pg-agent-1-at-the-right-gpg-manpage-in-SE.patch | 25 +
...ument-how-to-manually-shut-down-gpg-agent.patch | 41 +
...72-scd-minor-cleanup-to-merge-other-works.patch | 218 +++++
.../0073-scd-Support-ECC-key-generation.patch | 309 +++++++
...Make-use-of-default_errsource-in-exechelp.patch | 101 +++
...32-Extend-gnupg_create_inbound_pipe-et-al.patch | 93 ++
...Communicate-with-child-in-non-blocking-mo.patch | 52 ++
.../0077-common-Fix-copying-data-to-estreams.patch | 43 +
.../0078-agent-Add-card-option-for-READKEY.patch | 274 ++++++
.../patches/0079-g10-smartcard-keygen-change.patch | 341 +++++++
...-scd-GENKEY-updates-the-public-key-in-APP.patch | 568 ++++++++++++
debian/patches/0081-agent-g10-Fix-keygen.patch | 44 +
.../0082-agent-Fix-saving-with-FORCE-1.patch | 54 ++
.../patches/0083-Fix-use-cases-of-snprintf.patch | 999 +++++++++++++++++++++
.../0084-g10-Support-ECC-for-gen_card_key.patch | 126 +++
...10-Don-t-ask-keysize-for-for-non-RSA-card.patch | 108 +++
.../0086-scd-Fix-segfault-changing-key-attr.patch | 33 +
debian/patches/0087-g10-scd-Fix-ECC-keygen.patch | 236 +++++
...-Write-first-keybox-record-in-binary-mode.patch | 27 +
.../0089-g10-More-card-key-generation-change.patch | 161 ++++
.../0090-g10-Fix-card-keygen-for-decryption.patch | 29 +
...091-common-Fix-openpgp_is_curve_supported.patch | 30 +
...scd-Use-canonical-curve-name-of-libgcrypt.patch | 318 +++++++
...-Slightly-change-structure-of-cmd_readkey.patch | 117 +++
...or-cleanup-for-recent-change-in-findkey.c.patch | 32 +
.../0095-gpg-Replace-two-sprintf-calls.patch | 54 ++
...-w32-Fix-relaying-pinentry-user-data-fix-.patch | 190 ++++
debian/patches/series | 33 +
34 files changed, 5275 insertions(+)
diff --git a/debian/patches/0064-tools-Fix-error-handling.patch b/debian/patches/0064-tools-Fix-error-handling.patch
new file mode 100644
index 0000000..02f33c5
--- /dev/null
+++ b/debian/patches/0064-tools-Fix-error-handling.patch
@@ -0,0 +1,34 @@
+From: Justus Winter <justus at g10code.com>
+Date: Fri, 7 Oct 2016 12:52:09 +0200
+Subject: tools: Fix error handling.
+
+* tools/gpgtar-create.c (gpgtar_create): Do not crash if opening the
+tarball failed.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+---
+ tools/gpgtar-create.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c
+index 6adc1f5..6780eff 100644
+--- a/tools/gpgtar-create.c
++++ b/tools/gpgtar-create.c
+@@ -853,8 +853,6 @@ gpgtar_create (char **inpattern, int encrypt, int sign)
+ if (!outstream)
+ {
+ err = gpg_error_from_syserror ();
+- log_error (_("can't create '%s': %s\n"),
+- opt.outfile, gpg_strerror (err));
+ goto leave;
+ }
+ }
+@@ -958,7 +956,7 @@ gpgtar_create (char **inpattern, int encrypt, int sign)
+ if (err)
+ {
+ log_error ("creating tarball '%s' failed: %s\n",
+- es_fname_get (outstream), gpg_strerror (err));
++ opt.outfile ? opt.outfile : "-", gpg_strerror (err));
+ if (outstream && outstream != es_stdout)
+ es_fclose (outstream);
+ if (cipher_stream && cipher_stream != es_stdout)
diff --git a/debian/patches/0065-g10-Fix-a-column-s-type-in-TOFU-DB.patch b/debian/patches/0065-g10-Fix-a-column-s-type-in-TOFU-DB.patch
new file mode 100644
index 0000000..8e3d625
--- /dev/null
+++ b/debian/patches/0065-g10-Fix-a-column-s-type-in-TOFU-DB.patch
@@ -0,0 +1,29 @@
+From: "Neal H. Walfield" <neal at g10code.com>
+Date: Wed, 12 Oct 2016 21:37:34 +0200
+Subject: g10: Fix a column's type in TOFU DB.
+
+* g10/tofu.c (initdb): Change policy from a boolean to an integer.
+
+--
+Signed-off-by: Neal H. Walfield <neal at g10code.com>
+Reported-by: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
+
+Note: sqlite ignores type information so this change has no real
+impact.
+---
+ g10/tofu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/g10/tofu.c b/g10/tofu.c
+index ef14e85..87c7e87 100644
+--- a/g10/tofu.c
++++ b/g10/tofu.c
+@@ -567,7 +567,7 @@ initdb (sqlite3 *db)
+ "create table bindings\n"
+ " (oid INTEGER PRIMARY KEY AUTOINCREMENT,\n"
+ " fingerprint TEXT, email TEXT, user_id TEXT, time INTEGER,\n"
+- " policy BOOLEAN CHECK (policy in (%d, %d, %d, %d, %d)),\n"
++ " policy INTEGER CHECK (policy in (%d, %d, %d, %d, %d)),\n"
+ " conflict STRING,\n"
+ " unique (fingerprint, email));\n"
+ "create index bindings_fingerprint_email\n"
diff --git a/debian/patches/0066-agent-Move-inotify-code-to-common-and-improve-it.patch b/debian/patches/0066-agent-Move-inotify-code-to-common-and-improve-it.patch
new file mode 100644
index 0000000..b7270cd
--- /dev/null
+++ b/debian/patches/0066-agent-Move-inotify-code-to-common-and-improve-it.patch
@@ -0,0 +1,335 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Sat, 15 Oct 2016 21:35:05 +0200
+Subject: agent: Move inotify code to common and improve it.
+
+* common/sysutils.c: Include sys/inotify.h.
+(my_error_from_syserror, my_error): New.
+(gnupg_inotify_watch_socket): New.
+(gnupg_inotify_has_name): New.
+* agent/gpg-agent.c: Do not include sys/inotify.h.
+(my_inotify_is_name): Remove.
+(handle_connections): Remove HAVE_INOTIFY_INIT protected code and use
+the new functions.
+--
+
+When removing not a simple socket file but the entire directory the
+old code missed most events and thus did not worked properly.
+
+IN_DELETE_SELF has also been added to the watch list to detect a
+removal of the directory. However, in all tests that event was not
+triggered. The only way it could be triggered was by not watching
+the socket dir but an arbitary directory and rmdir that.
+
+GnuPG-bug-id: 2756
+Signed-off-by: Werner Koch <wk at gnupg.org>
+---
+ agent/gpg-agent.c | 65 ++++------------------------
+ common/sysutils.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ common/sysutils.h | 4 ++
+ 3 files changed, 140 insertions(+), 56 deletions(-)
+
+diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
+index 44a6bbb..0146d85 100644
+--- a/agent/gpg-agent.c
++++ b/agent/gpg-agent.c
+@@ -47,9 +47,6 @@
+ #ifdef HAVE_SIGNAL_H
+ # include <signal.h>
+ #endif
+-#ifdef HAVE_INOTIFY_INIT
+-# include <sys/inotify.h>
+-#endif /*HAVE_INOTIFY_INIT*/
+ #include <npth.h>
+ #ifdef HAVE_PRCTL
+ # include <sys/prctl.h>
+@@ -2724,31 +2721,6 @@ start_connection_thread_ssh (void *arg)
+ }
+
+
+-#ifdef HAVE_INOTIFY_INIT
+-/* Read an inotify event and return true if it matches NAME. */
+-static int
+-my_inotify_is_name (int fd, const char *name)
+-{
+- union {
+- struct inotify_event ev;
+- char _buf[sizeof (struct inotify_event) + 100 + 1];
+- } buf;
+- int n;
+-
+- n = npth_read (fd, &buf, sizeof buf);
+- if (n < sizeof (struct inotify_event))
+- return 0;
+- if (buf.ev.len < strlen (name)+1)
+- return 0;
+- if (strcmp (buf.ev.name, name))
+- return 0; /* Not the desired file. */
+-
+- return 1; /* Found. */
+-}
+-#endif /*HAVE_INOTIFY_INIT*/
+-
+-
+-
+ /* Connection handler loop. Wait for connection requests and spawn a
+ thread after accepting a connection. */
+ static void
+@@ -2757,6 +2729,7 @@ handle_connections (gnupg_fd_t listen_fd,
+ gnupg_fd_t listen_fd_browser,
+ gnupg_fd_t listen_fd_ssh)
+ {
++ gpg_error_t err;
+ npth_attr_t tattr;
+ struct sockaddr_un paddr;
+ socklen_t plen;
+@@ -2772,9 +2745,7 @@ handle_connections (gnupg_fd_t listen_fd,
+ HANDLE events[2];
+ unsigned int events_set;
+ #endif
+-#ifdef HAVE_INOTIFY_INIT
+- int my_inotify_fd;
+-#endif /*HAVE_INOTIFY_INIT*/
++ int my_inotify_fd = -1;
+ struct {
+ const char *name;
+ void *(*func) (void *arg);
+@@ -2812,27 +2783,14 @@ handle_connections (gnupg_fd_t listen_fd,
+ # endif
+ #endif
+
+-#ifdef HAVE_INOTIFY_INIT
+ if (disable_check_own_socket)
+ my_inotify_fd = -1;
+- else if ((my_inotify_fd = inotify_init ()) == -1)
+- log_info ("error enabling fast daemon termination: %s\n",
+- strerror (errno));
+- else
++ else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name)))
+ {
+- /* We need to watch the directory for the file becuase there
+- * won't be an IN_DELETE_SELF for a socket file. */
+- char *slash = strrchr (socket_name, '/');
+- log_assert (slash && slash[1]);
+- *slash = 0;
+- if (inotify_add_watch (my_inotify_fd, socket_name, IN_DELETE) == -1)
+- {
+- close (my_inotify_fd);
+- my_inotify_fd = -1;
+- }
+- *slash = '/';
++ if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
++ log_info ("error enabling fast daemon termination: %s\n",
++ gpg_strerror (err));
+ }
+-#endif /*HAVE_INOTIFY_INIT*/
+
+ /* On Windows we need to fire up a separate thread to listen for
+ requests from Putty (an SSH client), so we can replace Putty's
+@@ -2875,14 +2833,12 @@ handle_connections (gnupg_fd_t listen_fd,
+ if (FD2INT (listen_fd_ssh) > nfd)
+ nfd = FD2INT (listen_fd_ssh);
+ }
+-#ifdef HAVE_INOTIFY_INIT
+ if (my_inotify_fd != -1)
+ {
+ FD_SET (my_inotify_fd, &fdset);
+ if (my_inotify_fd > nfd)
+ nfd = my_inotify_fd;
+ }
+-#endif /*HAVE_INOTIFY_INIT*/
+
+ listentbl[0].l_fd = listen_fd;
+ listentbl[1].l_fd = listen_fd_extra;
+@@ -2957,14 +2913,13 @@ handle_connections (gnupg_fd_t listen_fd,
+ ctrl_t ctrl;
+ npth_t thread;
+
+-#ifdef HAVE_INOTIFY_INIT
+- if (my_inotify_fd != -1 && FD_ISSET (my_inotify_fd, &read_fdset)
+- && my_inotify_is_name (my_inotify_fd, GPG_AGENT_SOCK_NAME))
++ if (my_inotify_fd != -1
++ && FD_ISSET (my_inotify_fd, &read_fdset)
++ && gnupg_inotify_has_name (my_inotify_fd, GPG_AGENT_SOCK_NAME))
+ {
+ shutdown_pending = 1;
+ log_info ("socket file has been removed - shutting down\n");
+ }
+-#endif /*HAVE_INOTIFY_INIT*/
+
+ for (idx=0; idx < DIM(listentbl); idx++)
+ {
+@@ -3012,10 +2967,8 @@ handle_connections (gnupg_fd_t listen_fd,
+ }
+ }
+
+-#ifdef HAVE_INOTIFY_INIT
+ if (my_inotify_fd != -1)
+ close (my_inotify_fd);
+-#endif /*HAVE_INOTIFY_INIT*/
+ cleanup ();
+ log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
+ npth_attr_destroy (&tattr);
+diff --git a/common/sysutils.c b/common/sysutils.c
+index 0f7b7f5..2e663bc 100644
+--- a/common/sysutils.c
++++ b/common/sysutils.c
+@@ -63,6 +63,9 @@
+ # endif
+ # include <windows.h>
+ #endif
++#ifdef HAVE_INOTIFY_INIT
++# include <sys/inotify.h>
++#endif /*HAVE_INOTIFY_INIT*/
+ #ifdef HAVE_NPTH
+ # include <npth.h>
+ #endif
+@@ -78,6 +81,20 @@
+ #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
+
+
++static GPGRT_INLINE gpg_error_t
++my_error_from_syserror (void)
++{
++ return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
++}
++
++static GPGRT_INLINE gpg_error_t
++my_error (int e)
++{
++ return gpg_err_make (default_errsource, (e));
++}
++
++
++
+ #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
+ #warning using trap_unaligned
+ static int
+@@ -929,3 +946,113 @@ w32_get_user_sid (void)
+ return sid;
+ }
+ #endif /*HAVE_W32_SYSTEM*/
++
++
++
++/* Support for inotify under Linux. */
++
++/* Store a new inotify file handle for SOCKET_NAME at R_FD or return
++ * an error code. */
++gpg_error_t
++gnupg_inotify_watch_socket (int *r_fd, const char *socket_name)
++{
++#if HAVE_INOTIFY_INIT
++ gpg_error_t err;
++ char *fname;
++ int fd;
++ char *p;
++
++ *r_fd = -1;
++
++ fname = xtrystrdup (socket_name);
++ if (!fname)
++ return my_error_from_syserror ();
++
++ fd = inotify_init ();
++ if (fd == -1)
++ {
++ err = my_error_from_syserror ();
++ xfree (fname);
++ return err;
++ }
++
++ /* We need to watch the directory for the file because there won't
++ * be an IN_DELETE_SELF for a socket file. To handle a removal of
++ * the directory we also watch the directory itself. */
++ p = strrchr (fname, '/');
++ if (p)
++ *p = 0;
++ if (inotify_add_watch (fd, fname,
++ (IN_DELETE|IN_DELETE_SELF|IN_EXCL_UNLINK)) == -1)
++ {
++ err = my_error_from_syserror ();
++ close (fd);
++ xfree (fname);
++ return err;
++ }
++
++ xfree (fname);
++
++ *r_fd = fd;
++ return 0;
++#else /*!HAVE_INOTIFY_INIT*/
++
++ (void)socket_name;
++ *r_fd = -1;
++ return my_error (GPG_ERR_NOT_SUPPORTED);
++
++#endif /*!HAVE_INOTIFY_INIT*/
++}
++
++
++/* Read an inotify event and return true if it matches NAME or if it
++ * sees an IN_DELETE_SELF event for the directory of NAME. */
++int
++gnupg_inotify_has_name (int fd, const char *name)
++{
++#if USE_NPTH && HAVE_INOTIFY_INIT
++ union {
++ struct inotify_event ev;
++ char _buf[sizeof (struct inotify_event) + 255 + 1];
++ } buf;
++ struct inotify_event *evp;
++ int n;
++
++ n = npth_read (fd, &buf, sizeof buf);
++ /* log_debug ("notify read: n=%d\n", n); */
++ evp = &buf.ev;
++ while (n >= sizeof (struct inotify_event))
++ {
++ /* log_debug (" mask=%x len=%u name=(%s)\n", */
++ /* evp->mask, (unsigned int)evp->len, evp->len? evp->name:""); */
++ if ((evp->mask & IN_UNMOUNT))
++ {
++ /* log_debug (" found (dir unmounted)\n"); */
++ return 3; /* Directory was unmounted. */
++ }
++ if ((evp->mask & IN_DELETE_SELF))
++ {
++ /* log_debug (" found (dir removed)\n"); */
++ return 2; /* Directory was removed. */
++ }
++ if ((evp->mask & IN_DELETE))
++ {
++ if (evp->len >= strlen (name) && !strcmp (evp->name, name))
++ {
++ /* log_debug (" found (file removed)\n"); */
++ return 1; /* File was removed. */
++ }
++ }
++ n -= sizeof (*evp) + evp->len;
++ evp = (struct inotify_event *)((char*)evp + sizeof (*evp) + evp->len);
++ }
++
++#else /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/
++
++ (void)fd;
++ (void)name;
++
++#endif /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/
++
++ return 0; /* Not found. */
++}
+diff --git a/common/sysutils.h b/common/sysutils.h
+index ba66ce6..ea92e4c 100644
+--- a/common/sysutils.h
++++ b/common/sysutils.h
+@@ -67,6 +67,10 @@ int gnupg_setenv (const char *name, const char *value, int overwrite);
+ int gnupg_unsetenv (const char *name);
+ char *gnupg_getcwd (void);
+
++gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name);
++int gnupg_inotify_has_name (int fd, const char *name);
++
++
+ #ifdef HAVE_W32_SYSTEM
+ void *w32_get_user_sid (void);
+
diff --git a/debian/patches/0067-agent-Use-straightforward-names-for-the-default-sock.patch b/debian/patches/0067-agent-Use-straightforward-names-for-the-default-sock.patch
new file mode 100644
index 0000000..d9c9cab
--- /dev/null
+++ b/debian/patches/0067-agent-Use-straightforward-names-for-the-default-sock.patch
@@ -0,0 +1,35 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Sun, 16 Oct 2016 22:30:26 +0200
+Subject: agent: Use straightforward names for the default socket names.
+
+* configure.ac (GPG_AGENT_SOCK_NAME): Change name to *.extra.
+(GPG_AGENT_EXTRA_SOCK_NAME): Change name to *browser.
+--
+
+There has been quite some fuzz about the naming of the (new) default
+socket files. The used names do not match the names of the option.
+Because these are just names we now change the names to match the
+names of the options instead of changing the option names to something
+we can't agree upon.
+
+Signed-off-by: Werner Koch <wk at gnupg.org>
+---
+ configure.ac | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index acfd8c2..634a570 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1747,9 +1747,9 @@ AC_DEFINE_UNQUOTED(GPGTAR_NAME, "gpgtar", [The name of the gpgtar tool])
+
+ AC_DEFINE_UNQUOTED(GPG_AGENT_SOCK_NAME, "S.gpg-agent",
+ [The name of the agent socket])
+-AC_DEFINE_UNQUOTED(GPG_AGENT_EXTRA_SOCK_NAME, "S.gpg-agent.rstrd",
++AC_DEFINE_UNQUOTED(GPG_AGENT_EXTRA_SOCK_NAME, "S.gpg-agent.extra",
+ [The name of the agent socket for remote access])
+-AC_DEFINE_UNQUOTED(GPG_AGENT_BROWSER_SOCK_NAME, "S.gpg-agent.brwsr",
++AC_DEFINE_UNQUOTED(GPG_AGENT_BROWSER_SOCK_NAME, "S.gpg-agent.browser",
+ [The name of the agent socket for browsers])
+ AC_DEFINE_UNQUOTED(GPG_AGENT_SSH_SOCK_NAME, "S.gpg-agent.ssh",
+ [The name of the agent socket for ssh])
diff --git a/debian/patches/0068-gpgconf-Fix-for-homedir.patch b/debian/patches/0068-gpgconf-Fix-for-homedir.patch
new file mode 100644
index 0000000..23fb7b4
--- /dev/null
+++ b/debian/patches/0068-gpgconf-Fix-for-homedir.patch
@@ -0,0 +1,158 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Mon, 17 Oct 2016 11:36:45 +0900
+Subject: gpgconf: Fix for --homedir.
+
+* tools/gpgconf-comp.c (gpg_agent_runtime_change,
+scdaemon_runtime_change, dirmngr_runtime_change): Provide the homedir
+arguments by --homedir when it's not default.
+
+--
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ tools/gpgconf-comp.c | 80 +++++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 60 insertions(+), 20 deletions(-)
+
+diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c
+index 82b5325..8bf3086 100644
+--- a/tools/gpgconf-comp.c
++++ b/tools/gpgconf-comp.c
+@@ -1088,33 +1088,48 @@ struct error_line_s
+ static void
+ gpg_agent_runtime_change (int killflag)
+ {
+- gpg_error_t err;
++ gpg_error_t err = 0;
+ const char *pgmname;
+- const char *argv[3];
++ const char *argv[5];
+ pid_t pid;
++ char *abs_homedir = NULL;
++ int i = 0;
+
+ pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
+- argv[0] = "--no-autostart";
+- argv[1] = killflag? "KILLAGENT" : "RELOADAGENT";
+- argv[2] = NULL;
++ if (!gnupg_default_homedir_p ())
++ {
++ abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
++ if (!abs_homedir)
++ err = gpg_error_from_syserror ();
+
+- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
++ argv[i++] = "--homedir";
++ argv[i++] = abs_homedir;
++ }
++ argv[i++] = "--no-autostart";
++ argv[i++] = killflag? "KILLAGENT" : "RELOADAGENT";
++ argv[i++] = NULL;
++
++ if (!err)
++ err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ if (!err)
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ if (err)
+ gc_error (0, 0, "error running '%s %s': %s",
+ pgmname, argv[1], gpg_strerror (err));
+ gnupg_release_process (pid);
++ xfree (abs_homedir);
+ }
+
+
+ static void
+ scdaemon_runtime_change (int killflag)
+ {
+- gpg_error_t err;
++ gpg_error_t err = 0;
+ const char *pgmname;
+- const char *argv[7];
++ const char *argv[9];
+ pid_t pid;
++ char *abs_homedir = NULL;
++ int i = 0;
+
+ (void)killflag; /* For scdaemon kill and reload are synonyms. */
+
+@@ -1124,45 +1139,70 @@ scdaemon_runtime_change (int killflag)
+ obviously a race condition but that should not harm too much. */
+
+ pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
+- argv[0] = "-s";
+- argv[1] = "--no-autostart";
+- argv[2] = "GETINFO scd_running";
+- argv[3] = "/if ${! $?}";
+- argv[4] = "scd killscd";
+- argv[5] = "/end";
+- argv[6] = NULL;
++ if (!gnupg_default_homedir_p ())
++ {
++ abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
++ if (!abs_homedir)
++ err = gpg_error_from_syserror ();
++
++ argv[i++] = "--homedir";
++ argv[i++] = abs_homedir;
++ }
++ argv[i++] = "-s";
++ argv[i++] = "--no-autostart";
++ argv[i++] = "GETINFO scd_running";
++ argv[i++] = "/if ${! $?}";
++ argv[i++] = "scd killscd";
++ argv[i++] = "/end";
++ argv[i++] = NULL;
+
+- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
++ if (!err)
++ err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ if (!err)
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ if (err)
+ gc_error (0, 0, "error running '%s %s': %s",
+ pgmname, argv[4], gpg_strerror (err));
+ gnupg_release_process (pid);
++ xfree (abs_homedir);
+ }
+
+
+ static void
+ dirmngr_runtime_change (int killflag)
+ {
+- gpg_error_t err;
++ gpg_error_t err = 0;
+ const char *pgmname;
+- const char *argv[4];
++ const char *argv[6];
+ pid_t pid;
++ char *abs_homedir = NULL;
+
+ pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
+ argv[0] = "--no-autostart";
+ argv[1] = "--dirmngr";
+ argv[2] = killflag? "KILLDIRMNGR" : "RELOADDIRMNGR";
+- argv[3] = NULL;
++ if (gnupg_default_homedir_p ())
++ argv[3] = NULL;
++ else
++ {
++ abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
++ if (!abs_homedir)
++ err = gpg_error_from_syserror ();
+
+- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
++ argv[3] = "--homedir";
++ argv[4] = abs_homedir;
++ argv[5] = NULL;
++ }
++
++ if (!err)
++ err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ if (!err)
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ if (err)
+ gc_error (0, 0, "error running '%s %s': %s",
+ pgmname, argv[2], gpg_strerror (err));
+ gnupg_release_process (pid);
++ xfree (abs_homedir);
+ }
+
+
diff --git a/debian/patches/0069-scd-Fix-keytocard-for-ECC.patch b/debian/patches/0069-scd-Fix-keytocard-for-ECC.patch
new file mode 100644
index 0000000..ef6ce96
--- /dev/null
+++ b/debian/patches/0069-scd-Fix-keytocard-for-ECC.patch
@@ -0,0 +1,28 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Mon, 17 Oct 2016 12:02:28 +0900
+Subject: scd: Fix keytocard for ECC.
+
+* scd/app-openpgp.c (build_ecc_privkey_template): Size can be greater
+than 128 when it comes with public key for curve of larger field.
+
+--
+
+Reported-by: Arnaud Fontaine <arnaud.fontaine at ssi.gouv.fr>
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ scd/app-openpgp.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index 563a045..ef335fe 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -2689,6 +2689,8 @@ build_ecc_privkey_template (app_t app, int keyno,
+ + privkey_len
+ + suffix_len
+ + datalen);
++ if (exthdr_len + privkey_len + suffix_len + datalen >= 128)
++ template_size++;
+ tp = template = xtrymalloc_secure (template_size);
+ if (!template)
+ return gpg_error_from_syserror ();
diff --git a/debian/patches/0070-doc-Point-gpg-agent-1-at-the-right-gpg-manpage-in-SE.patch b/debian/patches/0070-doc-Point-gpg-agent-1-at-the-right-gpg-manpage-in-SE.patch
new file mode 100644
index 0000000..f6edf8d
--- /dev/null
+++ b/debian/patches/0070-doc-Point-gpg-agent-1-at-the-right-gpg-manpage-in-SE.patch
@@ -0,0 +1,25 @@
+From: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
+Date: Fri, 14 Oct 2016 02:23:37 -0400
+Subject: doc: Point gpg-agent(1) at the right gpg manpage in SEE ALSO.
+
+* doc/gpg-agent.texi (SEE ALSO): refer to @gpgname, instead of
+ hard-coding "gpg2".
+
+Signed-off-by: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
+---
+ doc/gpg-agent.texi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
+index 232e060..0645741 100644
+--- a/doc/gpg-agent.texi
++++ b/doc/gpg-agent.texi
+@@ -1516,7 +1516,7 @@ much slower or faster than the actual box.
+
+ @mansect see also
+ @ifset isman
+- at command{gpg2}(1),
++ at command{@gpgname}(1),
+ @command{gpgsm}(1),
+ @command{gpg-connect-agent}(1),
+ @command{scdaemon}(1)
diff --git a/debian/patches/0071-doc-Document-how-to-manually-shut-down-gpg-agent.patch b/debian/patches/0071-doc-Document-how-to-manually-shut-down-gpg-agent.patch
new file mode 100644
index 0000000..6362a8c
--- /dev/null
+++ b/debian/patches/0071-doc-Document-how-to-manually-shut-down-gpg-agent.patch
@@ -0,0 +1,41 @@
+From: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
+Date: Fri, 14 Oct 2016 12:42:24 -0400
+Subject: doc: Document how to manually shut down gpg-agent.
+
+* doc/gpg-agent.texi: document "gpgconf --kill gpg-agent" for manual
+ agent termination.
+
+This was requested in a side-comment in https://bugs.debian.org/840669
+
+Signed-off-by: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
+---
+ doc/gpg-agent.texi | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
+index 0645741..cc016f8 100644
+--- a/doc/gpg-agent.texi
++++ b/doc/gpg-agent.texi
+@@ -85,6 +85,14 @@ gpg-connect-agent /bye
+ @end example
+
+ @noindent
++If you want to manually terminate the currently-running agent, you can
++safely do so with:
++
++ at example
++gpgconf --kill gpg-agent
++ at end example
++
++ at noindent
+ @efindex GPG_TTY
+ You should always add the following lines to your @code{.bashrc} or
+ whatever initialization file is used for all shell invocations:
+@@ -1518,6 +1526,7 @@ much slower or faster than the actual box.
+ @ifset isman
+ @command{@gpgname}(1),
+ @command{gpgsm}(1),
++ at command{gpgconf}(1),
+ @command{gpg-connect-agent}(1),
+ @command{scdaemon}(1)
+ @end ifset
diff --git a/debian/patches/0072-scd-minor-cleanup-to-merge-other-works.patch b/debian/patches/0072-scd-minor-cleanup-to-merge-other-works.patch
new file mode 100644
index 0000000..c9eb33b
--- /dev/null
+++ b/debian/patches/0072-scd-minor-cleanup-to-merge-other-works.patch
@@ -0,0 +1,218 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Tue, 18 Oct 2016 20:40:09 +0900
+Subject: scd: minor cleanup to merge other works.
+
+* scd/iso7816.c (do_generate_keypair): Use const char * for DATA.
+(iso7816_generate_keypair, iso7816_read_public_key): Likewise.
+* scd/app-openpgp.c (get_public_key): Follow the change.
+(do_genkey): Ditto. Use ERR instead of RC. Use u32 for CREATED_AT.
+--
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ scd/app-openpgp.c | 58 +++++++++++++++++++++++++------------------------------
+ scd/iso7816.c | 9 ++++-----
+ scd/iso7816.h | 4 ++--
+ 3 files changed, 32 insertions(+), 39 deletions(-)
+
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index ef335fe..ba16255 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -1276,12 +1276,10 @@ get_public_key (app_t app, int keyno)
+ le_value = 256; /* Use legacy value. */
+ }
+
+- err = iso7816_read_public_key
+- (app->slot, exmode,
+- (const unsigned char*)(keyno == 0? "\xB6" :
+- keyno == 1? "\xB8" : "\xA4"), 2,
+- le_value,
+- &buffer, &buflen);
++ err = iso7816_read_public_key (app->slot, exmode,
++ (keyno == 0? "\xB6" :
++ keyno == 1? "\xB8" : "\xA4"),
++ 2, le_value, &buffer, &buflen);
+ if (err)
+ {
+ log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
+@@ -3534,13 +3532,13 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+ {
+- int rc;
++ gpg_error_t err;
+ char numbuf[30];
+ unsigned char fprbuf[20];
+ const unsigned char *keydata, *m, *e;
+ unsigned char *buffer = NULL;
+ size_t buflen, keydatalen, mlen, elen;
+- time_t created_at;
++ u32 created_at;
+ int keyno = atoi (keynostr) - 1;
+ int force = (flags & 1);
+ time_t start_at;
+@@ -3562,9 +3560,9 @@ 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 = does_key_exist (app, keyno, 1, force);
+- if (rc)
+- return rc;
++ err = does_key_exist (app, keyno, 1, force);
++ if (err)
++ return err;
+
+ /* Because we send the key parameter back via status lines we need
+ to put a limit on the max. allowed keysize. 2048 bit will
+@@ -3575,8 +3573,8 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ /* Prepare for key generation by verifying the Admin PIN. */
+- rc = verify_chv3 (app, pincb, pincb_arg);
+- if (rc)
++ err = verify_chv3 (app, pincb, pincb_arg);
++ if (err)
+ goto leave;
+
+ /* Test whether we will need extended length mode. (1900 is an
+@@ -3597,17 +3595,13 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+
+ log_info (_("please wait while key is being generated ...\n"));
+ start_at = time (NULL);
+- rc = iso7816_generate_keypair
+-/* # warning key generation temporary replaced by reading an existing key. */
+-/* rc = iso7816_read_public_key */
+- (app->slot, exmode,
+- (const unsigned char*)(keyno == 0? "\xB6" :
+- keyno == 1? "\xB8" : "\xA4"), 2,
+- le_value,
+- &buffer, &buflen);
+- if (rc)
++ err = iso7816_generate_keypair (app->slot, exmode,
++ (keyno == 0? "\xB6" :
++ keyno == 1? "\xB8" : "\xA4"),
++ 2, le_value, &buffer, &buflen);
++ if (err)
+ {
+- rc = gpg_error (GPG_ERR_CARD);
++ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("generating key failed\n"));
+ goto leave;
+ }
+@@ -3622,7 +3616,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
+ if (!keydata)
+ {
+- rc = gpg_error (GPG_ERR_CARD);
++ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the public key data\n"));
+ goto leave;
+ }
+@@ -3630,7 +3624,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+ if (!m)
+ {
+- rc = gpg_error (GPG_ERR_CARD);
++ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA modulus\n"));
+ goto leave;
+ }
+@@ -3640,15 +3634,15 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+ if (!e)
+ {
+- rc = gpg_error (GPG_ERR_CARD);
++ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA public exponent\n"));
+ goto leave;
+ }
+ /* log_printhex ("RSA e:", e, elen); */
+ send_key_data (ctrl, "e", e, elen);
+
+- created_at = createtime? createtime : gnupg_get_time ();
+- sprintf (numbuf, "%lu", (unsigned long)created_at);
++ created_at = (u32)(createtime? createtime : gnupg_get_time ());
++ sprintf (numbuf, "%u", created_at);
+ send_status_info (ctrl, "KEY-CREATED-AT",
+ numbuf, (size_t)strlen(numbuf), NULL, 0);
+
+@@ -3657,16 +3651,16 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+ ;
+
+- rc = store_fpr (app, keyno, (u32)created_at, fprbuf, PUBKEY_ALGO_RSA,
+- m, mlen, e, elen);
+- if (rc)
++ err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
++ m, mlen, e, elen);
++ if (err)
+ goto leave;
+ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+
+
+ leave:
+ xfree (buffer);
+- return rc;
++ return err;
+ }
+
+
+diff --git a/scd/iso7816.c b/scd/iso7816.c
+index 515e21f..28cd2eb 100644
+--- a/scd/iso7816.c
++++ b/scd/iso7816.c
+@@ -604,8 +604,7 @@ iso7816_internal_authenticate (int slot, int extended_mode,
+ (e.g. 4096 bytes), a value larger 256 used that value. */
+ static gpg_error_t
+ do_generate_keypair (int slot, int extended_mode, int read_only,
+- const unsigned char *data, size_t datalen,
+- int le,
++ const char *data, size_t datalen, int le,
+ unsigned char **result, size_t *resultlen)
+ {
+ int sw;
+@@ -617,7 +616,7 @@ do_generate_keypair (int slot, int extended_mode, int read_only,
+
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_GENERATE_KEYPAIR, read_only? 0x81:0x80, 0,
+- datalen, (const char*)data,
++ datalen, data,
+ le >= 0 && le < 256? 256:le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+@@ -635,7 +634,7 @@ do_generate_keypair (int slot, int extended_mode, int read_only,
+
+ gpg_error_t
+ iso7816_generate_keypair (int slot, int extended_mode,
+- const unsigned char *data, size_t datalen,
++ const char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen)
+ {
+@@ -646,7 +645,7 @@ iso7816_generate_keypair (int slot, int extended_mode,
+
+ gpg_error_t
+ iso7816_read_public_key (int slot, int extended_mode,
+- const unsigned char *data, size_t datalen,
++ const char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen)
+ {
+diff --git a/scd/iso7816.h b/scd/iso7816.h
+index 6dd1052..45cd416 100644
+--- a/scd/iso7816.h
++++ b/scd/iso7816.h
+@@ -100,11 +100,11 @@ gpg_error_t iso7816_internal_authenticate (int slot, int extended_mode,
+ int le,
+ unsigned char **result, size_t *resultlen);
+ gpg_error_t iso7816_generate_keypair (int slot, int extended_mode,
+- const unsigned char *data, size_t datalen,
++ const char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen);
+ gpg_error_t iso7816_read_public_key (int slot, int extended_mode,
+- const unsigned char *data, size_t datalen,
++ const char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen);
+ gpg_error_t iso7816_get_challenge (int slot,
diff --git a/debian/patches/0073-scd-Support-ECC-key-generation.patch b/debian/patches/0073-scd-Support-ECC-key-generation.patch
new file mode 100644
index 0000000..d65f47f
--- /dev/null
+++ b/debian/patches/0073-scd-Support-ECC-key-generation.patch
@@ -0,0 +1,309 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Tue, 18 Oct 2016 22:46:37 +0900
+Subject: scd: Support ECC key generation.
+
+* scd/app-openpgp.c (get_public_key): Fix a message.
+(change_keyattr_from_string, ecc_writekey): Call mpi_release sooner.
+(do_genkey): Add ECC support.
+
+--
+
+In OpenPGP card specification 3.0, ECC is introduced. So far, do_genkey
+only supported RSA. Since KDF spec. is needed to calculate the
+fingerprint, it is hard coded in app-openpgp.c. But it's defined by
+OpenPGP ECC (RFC-6637), and card does nothing with KDF in fact.
+
+Co-authored-by: Arnaud Fontaine <arnaud.fontaine at ssi.gouv.fr>
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ scd/app-openpgp.c | 198 +++++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 137 insertions(+), 61 deletions(-)
+
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index ba16255..09e4800 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -1318,7 +1318,7 @@ get_public_key (app_t app, int keyno)
+ if (!m)
+ {
+ err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the EC public point\n"));
++ log_error (_("response does not contain the EC public key\n"));
+ goto leave;
+ }
+ }
+@@ -2847,6 +2847,7 @@ change_keyattr_from_string (app_t app,
+ size_t oid_len;
+
+ oidstr = openpgp_curve_to_oid (string+n, NULL);
++ gcry_mpi_release (oid);
+ if (!oidstr)
+ {
+ err = gpg_error (GPG_ERR_INV_DATA);
+@@ -2864,7 +2865,6 @@ change_keyattr_from_string (app_t app,
+ string[0] = algo;
+ memcpy (string+1, oidbuf+1, oid_len-1);
+ err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
+- gcry_mpi_release (oid);
+ }
+ else
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+@@ -3355,13 +3355,14 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ if (err)
+ goto leave;
+ oidbuf = gcry_mpi_get_opaque (oid, &n);
+- oid_len = (n+7)/8;
+ if (!oidbuf)
+ {
+ err = gpg_error_from_syserror ();
+ gcry_mpi_release (oid);
+ goto leave;
+ }
++ gcry_mpi_release (oid);
++ oid_len = (n+7)/8;
+
+ if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
+ || app->app_local->keyattr[keyno].ecc.oid != oidstr
+@@ -3442,8 +3443,6 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
+
+ leave:
+- if (oidbuf)
+- gcry_mpi_release (oid);
+ return err;
+ }
+
+@@ -3535,16 +3534,15 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ gpg_error_t err;
+ char numbuf[30];
+ unsigned char fprbuf[20];
+- const unsigned char *keydata, *m, *e;
+ unsigned char *buffer = NULL;
+- size_t buflen, keydatalen, mlen, elen;
++ const unsigned char *keydata;
++ size_t buflen, keydatalen;
+ u32 created_at;
+ int keyno = atoi (keynostr) - 1;
+ int force = (flags & 1);
+ time_t start_at;
+- int exmode;
+- int le_value;
+- unsigned int keybits;
++ int exmode = 0;
++ int le_value = 256; /* Use legacy value. */
+
+ if (keyno < 0 || keyno > 2)
+ return gpg_error (GPG_ERR_INV_ID);
+@@ -3564,34 +3562,34 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ if (err)
+ return err;
+
+- /* Because we send the key parameter back via status lines we need
+- to put a limit on the max. allowed keysize. 2048 bit will
+- already lead to a 527 byte long status line and thus a 4096 bit
+- key would exceed the Assuan line length limit. */
+- keybits = app->app_local->keyattr[keyno].rsa.n_bits;
+- if (keybits > 4096)
+- return gpg_error (GPG_ERR_TOO_LARGE);
++ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
++ {
++ unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits;
++
++ /* Because we send the key parameter back via status lines we need
++ to put a limit on the max. allowed keysize. 2048 bit will
++ already lead to a 527 byte long status line and thus a 4096 bit
++ key would exceed the Assuan line length limit. */
++ if (keybits > 4096)
++ return gpg_error (GPG_ERR_TOO_LARGE);
++
++ /* Test whether we will need extended length mode. (1900 is an
++ arbitrary length which for sure fits into a short apdu.) */
++ if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
++ {
++ exmode = 1; /* Use extended length w/o a limit. */
++ le_value = app->app_local->extcap.max_rsp_data;
++ /* No need to check le_value because it comes from a 16 bit
++ value and thus can't create an overflow on a 32 bit
++ system. */
++ }
++ }
+
+ /* Prepare for key generation by verifying the Admin PIN. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+- goto leave;
++ return err;
+
+- /* Test whether we will need extended length mode. (1900 is an
+- arbitrary length which for sure fits into a short apdu.) */
+- if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
+- {
+- exmode = 1; /* Use extended length w/o a limit. */
+- le_value = app->app_local->extcap.max_rsp_data;
+- /* No need to check le_value because it comes from a 16 bit
+- value and thus can't create an overflow on a 32 bit
+- system. */
+- }
+- else
+- {
+- exmode = 0;
+- le_value = 256; /* Use legacy value. */
+- }
+
+ log_info (_("please wait while key is being generated ...\n"));
+ start_at = time (NULL);
+@@ -3601,9 +3599,8 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ 2, le_value, &buffer, &buflen);
+ if (err)
+ {
+- err = gpg_error (GPG_ERR_CARD);
+ log_error (_("generating key failed\n"));
+- goto leave;
++ return gpg_error (GPG_ERR_CARD);
+ }
+
+ {
+@@ -3621,38 +3618,117 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ goto leave;
+ }
+
+- m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+- if (!m)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the RSA modulus\n"));
+- goto leave;
+- }
+- /* log_printhex ("RSA n:", m, mlen); */
+- send_key_data (ctrl, "n", m, mlen);
+-
+- e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+- if (!e)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the RSA public exponent\n"));
+- goto leave;
+- }
+- /* log_printhex ("RSA e:", e, elen); */
+- send_key_data (ctrl, "e", e, elen);
+-
+ created_at = (u32)(createtime? createtime : gnupg_get_time ());
+ sprintf (numbuf, "%u", created_at);
+ send_status_info (ctrl, "KEY-CREATED-AT",
+ numbuf, (size_t)strlen(numbuf), NULL, 0);
+
+- for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+- ;
+- for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+- ;
++ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
++ {
++ const unsigned char *m, *e;
++ size_t mlen, elen;
++
++ m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
++ if (!m)
++ {
++ err = gpg_error (GPG_ERR_CARD);
++ log_error (_("response does not contain the RSA modulus\n"));
++ goto leave;
++ }
++ /* log_printhex ("RSA n:", m, mlen); */
++ send_key_data (ctrl, "n", m, mlen);
++
++ e = find_tlv (keydata, keydatalen, 0x0082, &elen);
++ if (!e)
++ {
++ err = gpg_error (GPG_ERR_CARD);
++ log_error (_("response does not contain the RSA public exponent\n"));
++ goto leave;
++ }
++ /* log_printhex ("RSA e:", e, elen); */
++ send_key_data (ctrl, "e", e, elen);
++
++ for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
++ ;
++ for (; elen && !*e; elen--, e++) /* strip leading zeroes */
++ ;
++
++ err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
++ m, mlen, e, elen);
++ }
++ else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
++ {
++ const unsigned char *ecc_q;
++ size_t ecc_q_len;
++ gcry_mpi_t oid;
++ int n;
++ const unsigned char *oidbuf;
++ size_t oid_len;
++ int algo;
++
++ ecc_q = find_tlv (keydata, keydatalen, 0x0086, &ecc_q_len);
++ if (!ecc_q)
++ {
++ err = gpg_error (GPG_ERR_CARD);
++ log_error (_("response does not contain the EC public key\n"));
++ goto leave;
++ }
++
++ err = openpgp_oid_from_str (app->app_local->keyattr[keyno].ecc.oid, &oid);
++ if (err)
++ goto leave;
++
++ oidbuf = gcry_mpi_get_opaque (oid, &n);
++ if (!oidbuf)
++ {
++ err = gpg_error_from_syserror ();
++ gcry_mpi_release (oid);
++ goto leave;
++ }
++ gcry_mpi_release (oid);
++ oid_len = (n+7)/8;
++
++ if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
++ { /* Prepend 0x40 prefix. */
++ unsigned char *q = xtrymalloc (ecc_q_len + 1);
++
++ if (!q)
++ {
++ err = gpg_error_from_syserror ();
++ goto leave;
++ }
++ *q = 0x40;
++ memcpy (q+1, ecc_q, ecc_q_len);
++ send_key_data (ctrl, "q", q, ecc_q_len + 1);
++ xfree (q);
++ }
++ else
++ {
++ /* strip leading zeroes */
++ for (; ecc_q_len && !*ecc_q; ecc_q_len--, ecc_q++)
++ ;
++ send_key_data (ctrl, "q", ecc_q, ecc_q_len);
++ }
++
++ send_key_data (ctrl, "curve", oidbuf, oid_len);
++
++ if (keyno == 1)
++ {
++ send_key_data (ctrl, "kdf", "\x03\x01\x08\x07", (size_t)4);
++ algo = PUBKEY_ALGO_ECDH;
++ }
++ else
++ {
++ if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
++ algo = PUBKEY_ALGO_EDDSA;
++ else
++ algo = PUBKEY_ALGO_ECDSA;
++ }
++
++ err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
++ ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
++ }
+
+- err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
+- m, mlen, e, elen);
+ if (err)
+ goto leave;
+ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
diff --git a/debian/patches/0074-common-w32-Make-use-of-default_errsource-in-exechelp.patch b/debian/patches/0074-common-w32-Make-use-of-default_errsource-in-exechelp.patch
new file mode 100644
index 0000000..09225a4
--- /dev/null
+++ b/debian/patches/0074-common-w32-Make-use-of-default_errsource-in-exechelp.patch
@@ -0,0 +1,101 @@
+From: Justus Winter <justus at g10code.com>
+Date: Tue, 18 Oct 2016 14:01:53 +0200
+Subject: common,w32: Make use of default_errsource in exechelp.
+
+* common/exechelp-posix.c (my_error_from_syserror, my_error): New.
+Use them instead of gpg_error and gpg_error_from_syserror.
+
+Fixes-commit: 96c7901ec1c79be732570811223d3ea54875abfe
+Signed-off-by: Justus Winter <justus at g10code.com>
+---
+ common/exechelp-w32.c | 28 +++++++++++++++++++++-------
+ 1 file changed, 21 insertions(+), 7 deletions(-)
+
+diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c
+index b2d2457..418eb9b 100644
+--- a/common/exechelp-w32.c
++++ b/common/exechelp-w32.c
+@@ -84,6 +84,20 @@
+ # define handle_to_pid(a) ((int)(a))
+
+
++/* Helper */
++static inline gpg_error_t
++my_error_from_syserror (void)
++{
++ return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
++}
++
++static inline gpg_error_t
++my_error (int errcode)
++{
++ return gpg_err_make (default_errsource, errcode);
++}
++
++
+ /* Return the maximum number of currently allowed open file
+ descriptors. Only useful on POSIX systems but returns a value on
+ other systems too. */
+@@ -219,7 +233,7 @@ build_w32_commandline (const char *pgmname, const char * const *argv,
+
+ buf = p = xtrymalloc (n);
+ if (!buf)
+- return gpg_error_from_syserror ();
++ return my_error_from_syserror ();
+
+ p = build_w32_commandline_copy (p, pgmname);
+ for (i=0; argv[i]; i++)
+@@ -293,7 +307,7 @@ do_create_pipe (int filedes[2], int flags)
+ HANDLE fds[2];
+
+ filedes[0] = filedes[1] = -1;
+- err = gpg_error (GPG_ERR_GENERAL);
++ err = my_error (GPG_ERR_GENERAL);
+ if (!create_inheritable_pipe (fds, flags))
+ {
+ filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY);
+@@ -662,7 +676,7 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
+ ))
+ {
+ log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
+- err = gpg_error (GPG_ERR_GENERAL);
++ err = my_error (GPG_ERR_GENERAL);
+ }
+ else
+ err = 0;
+@@ -707,7 +721,7 @@ gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
+
+ procs = xtrycalloc (count, sizeof *procs);
+ if (procs == NULL)
+- return gpg_error_from_syserror ();
++ return my_error_from_syserror ();
+
+ for (i = 0; i < count; i++)
+ {
+@@ -715,7 +729,7 @@ gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
+ r_exitcodes[i] = -1;
+
+ if (pids[i] == (pid_t)(-1))
+- return gpg_error (GPG_ERR_INV_VALUE);
++ return my_error (GPG_ERR_INV_VALUE);
+
+ procs[i] = fd_to_handle (pids[i]);
+ }
+@@ -818,7 +832,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
+ (void)envp;
+
+ if (access (pgmname, X_OK))
+- return gpg_error_from_syserror ();
++ return my_error_from_syserror ();
+
+ /* Prepare security attributes. */
+ memset (&sec_attr, 0, sizeof sec_attr );
+@@ -856,7 +870,7 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
+ {
+ log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
+ xfree (cmdline);
+- return gpg_error (GPG_ERR_GENERAL);
++ return my_error (GPG_ERR_GENERAL);
+ }
+ xfree (cmdline);
+ cmdline = NULL;
diff --git a/debian/patches/0075-common-w32-Extend-gnupg_create_inbound_pipe-et-al.patch b/debian/patches/0075-common-w32-Extend-gnupg_create_inbound_pipe-et-al.patch
new file mode 100644
index 0000000..55df5d7
--- /dev/null
+++ b/debian/patches/0075-common-w32-Extend-gnupg_create_inbound_pipe-et-al.patch
@@ -0,0 +1,93 @@
+From: Justus Winter <justus at g10code.com>
+Date: Tue, 18 Oct 2016 13:55:12 +0200
+Subject: common,w32: Extend gnupg_create_inbound_pipe et al.
+
+* common/exechelp-w32.c (do_create_pipe): Rename, add arguments, and
+create a stream if reqested.
+(gnupg_create_inbound_pipe): Use the extended function to open the
+stream if requested.
+(gnupg_create_outbound_pipe): Likewise.
+(gnupg_create_pipe): Update call site.
+
+Fixes-commit: 5d991e333a1885adc40abd9d00c01fec4bd5d9d7
+Signed-off-by: Justus Winter <justus at g10code.com>
+---
+ common/exechelp-w32.c | 37 +++++++++++++++++++++++++++----------
+ 1 file changed, 27 insertions(+), 10 deletions(-)
+
+diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c
+index 418eb9b..c5d6b08 100644
+--- a/common/exechelp-w32.c
++++ b/common/exechelp-w32.c
+@@ -301,7 +301,8 @@ w32_open_null (int for_write)
+
+
+ static gpg_error_t
+-do_create_pipe (int filedes[2], int flags)
++create_pipe_and_estream (int filedes[2], int flags,
++ estream_t *r_fp, int outbound, int nonblock)
+ {
+ gpg_error_t err = 0;
+ HANDLE fds[2];
+@@ -330,6 +331,25 @@ do_create_pipe (int filedes[2], int flags)
+ err = 0;
+ }
+ }
++
++ if (! err && r_fp)
++ {
++ if (!outbound)
++ *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r");
++ else
++ *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w");
++ if (!*r_fp)
++ {
++ err = my_error_from_syserror ();
++ log_error (_("error creating a stream for a pipe: %s\n"),
++ gpg_strerror (err));
++ close (filedes[0]);
++ close (filedes[1]);
++ filedes[0] = filedes[1] = -1;
++ return err;
++ }
++ }
++
+ return err;
+ }
+
+@@ -339,10 +359,8 @@ do_create_pipe (int filedes[2], int flags)
+ gpg_error_t
+ gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
+ {
+- if (r_fp)
+- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+- else
+- return do_create_pipe (filedes, INHERIT_WRITE);
++ return create_pipe_and_estream (filedes, INHERIT_WRITE,
++ r_fp, 0, nonblock);
+ }
+
+
+@@ -352,10 +370,8 @@ gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
+ gpg_error_t
+ gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
+ {
+- if (r_fp)
+- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+- else
+- return do_create_pipe (filedes, INHERIT_READ);
++ return create_pipe_and_estream (filedes, INHERIT_READ,
++ r_fp, 1, nonblock);
+ }
+
+
+@@ -364,7 +380,8 @@ gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
+ gpg_error_t
+ gnupg_create_pipe (int filedes[2])
+ {
+- return do_create_pipe (filedes, INHERIT_BOTH);
++ return create_pipe_and_estream (filedes, INHERIT_BOTH,
++ NULL, 0, 0);
+ }
+
+
diff --git a/debian/patches/0076-common-w32-Communicate-with-child-in-non-blocking-mo.patch b/debian/patches/0076-common-w32-Communicate-with-child-in-non-blocking-mo.patch
new file mode 100644
index 0000000..31dea61
--- /dev/null
+++ b/debian/patches/0076-common-w32-Communicate-with-child-in-non-blocking-mo.patch
@@ -0,0 +1,52 @@
+From: Justus Winter <justus at g10code.com>
+Date: Tue, 18 Oct 2016 14:04:54 +0200
+Subject: common,w32: Communicate with child in non-blocking mode.
+
+* common/exechelp-w32.c (gnupg_spawn_process): Open streams in
+non-blocking mode if requested.
+
+Fixes-commit: 83811e3f1f0c615b2b63bafdb49a35a0fc198088
+Signed-off-by: Justus Winter <justus at g10code.com>
+---
+ common/exechelp-w32.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c
+index c5d6b08..19e4d9e 100644
+--- a/common/exechelp-w32.c
++++ b/common/exechelp-w32.c
+@@ -418,6 +418,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
+ int i;
+ es_syshd_t syshd;
+ gpg_err_source_t errsource = default_errsource;
++ int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
+
+ (void)except; /* Not yet used. */
+
+@@ -440,7 +441,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
+
+ syshd.type = ES_SYSHD_HANDLE;
+ syshd.u.handle = inpipe[1];
+- infp = es_sysopen (&syshd, "w");
++ infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
+ if (!infp)
+ {
+ err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+@@ -464,7 +465,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
+
+ syshd.type = ES_SYSHD_HANDLE;
+ syshd.u.handle = outpipe[0];
+- outfp = es_sysopen (&syshd, "r");
++ outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
+ if (!outfp)
+ {
+ err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+@@ -494,7 +495,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
+
+ syshd.type = ES_SYSHD_HANDLE;
+ syshd.u.handle = errpipe[0];
+- errfp = es_sysopen (&syshd, "r");
++ errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
+ if (!errfp)
+ {
+ err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
diff --git a/debian/patches/0077-common-Fix-copying-data-to-estreams.patch b/debian/patches/0077-common-Fix-copying-data-to-estreams.patch
new file mode 100644
index 0000000..9992ab1
--- /dev/null
+++ b/debian/patches/0077-common-Fix-copying-data-to-estreams.patch
@@ -0,0 +1,43 @@
+From: Justus Winter <justus at g10code.com>
+Date: Tue, 18 Oct 2016 17:57:19 +0200
+Subject: common: Fix copying data to estreams.
+
+* common/exectool.c (copy_buffer_do_copy): Correctly account for
+partially written data in the event of errors.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+---
+ common/exectool.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/common/exectool.c b/common/exectool.c
+index e46071c..cf54efe 100644
+--- a/common/exectool.c
++++ b/common/exectool.c
+@@ -248,7 +248,14 @@ copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
+ return 0; /* Done copying. */
+
+
++ nwritten = 0;
+ err = sink? es_write (sink, c->writep, c->nread, &nwritten) : 0;
++
++ assert (nwritten <= c->nread);
++ c->writep += nwritten;
++ c->nread -= nwritten;
++ assert (c->writep - c->buffer <= sizeof c->buffer);
++
+ if (err)
+ {
+ if (errno == EAGAIN)
+@@ -257,11 +264,6 @@ copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
+ return my_error_from_syserror ();
+ }
+
+- assert (nwritten <= c->nread);
+- c->writep += nwritten;
+- c->nread -= nwritten;
+- assert (c->writep - c->buffer <= sizeof c->buffer);
+-
+ if (sink && es_fflush (sink) && errno != EAGAIN)
+ err = my_error_from_syserror ();
+
diff --git a/debian/patches/0078-agent-Add-card-option-for-READKEY.patch b/debian/patches/0078-agent-Add-card-option-for-READKEY.patch
new file mode 100644
index 0000000..c860bb0
--- /dev/null
+++ b/debian/patches/0078-agent-Add-card-option-for-READKEY.patch
@@ -0,0 +1,274 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Thu, 20 Oct 2016 12:05:15 +0900
+Subject: agent: Add --card option for READKEY.
+
+* agent/findkey.c (agent_write_shadow_key): New.
+* agent/command-ssh.c (card_key_available): Use agent_write_shadow_key.
+* agent/learncard.c (agent_handle_learn): Likewise.
+* agent/command.c (cmd_readkey): Add --card option.
+--
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ agent/agent.h | 3 +++
+ agent/command-ssh.c | 32 +------------------------
+ agent/command.c | 69 +++++++++++++++++++++++++++++++++++++++++++----------
+ agent/findkey.c | 36 ++++++++++++++++++++++++++++
+ agent/learncard.c | 30 +++--------------------
+ 5 files changed, 100 insertions(+), 70 deletions(-)
+
+diff --git a/agent/agent.h b/agent/agent.h
+index fe5ffba..a3ec457 100644
+--- a/agent/agent.h
++++ b/agent/agent.h
+@@ -490,6 +490,9 @@ gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo,
+ const unsigned char *s2ksalt,
+ unsigned int s2kcount,
+ unsigned char *key, size_t keylen);
++gpg_error_t agent_write_shadow_key (const unsigned char *grip,
++ const char *serialno, const char *keyid,
++ const unsigned char *pkbuf, int force);
+
+
+ /*-- trustlist.c --*/
+diff --git a/agent/command-ssh.c b/agent/command-ssh.c
+index 83a27ed..dd74d2d 100644
+--- a/agent/command-ssh.c
++++ b/agent/command-ssh.c
+@@ -2474,39 +2474,9 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
+ if ( agent_key_available (grip) )
+ {
+ /* (Shadow)-key is not available in our key storage. */
+- unsigned char *shadow_info;
+- unsigned char *tmp;
+-
+- shadow_info = make_shadow_info (serialno, authkeyid);
+- if (!shadow_info)
+- {
+- err = gpg_error_from_syserror ();
+- xfree (pkbuf);
+- gcry_sexp_release (s_pk);
+- xfree (serialno);
+- xfree (authkeyid);
+- return err;
+- }
+- err = agent_shadow_key (pkbuf, shadow_info, &tmp);
+- xfree (shadow_info);
+- if (err)
+- {
+- log_error (_("shadowing the key failed: %s\n"), gpg_strerror (err));
+- xfree (pkbuf);
+- gcry_sexp_release (s_pk);
+- xfree (serialno);
+- xfree (authkeyid);
+- return err;
+- }
+- xfree (pkbuf);
+- pkbuf = tmp;
+- pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
+- assert (pkbuflen);
+-
+- err = agent_write_private_key (grip, pkbuf, pkbuflen, 0);
++ err = agent_write_shadow_key (grip, serialno, authkeyid, pkbuf, 0);
+ if (err)
+ {
+- log_error (_("error writing key: %s\n"), gpg_strerror (err));
+ xfree (pkbuf);
+ gcry_sexp_release (s_pk);
+ xfree (serialno);
+diff --git a/agent/command.c b/agent/command.c
+index 9522f89..12d90ed 100644
+--- a/agent/command.c
++++ b/agent/command.c
+@@ -981,8 +981,10 @@ cmd_genkey (assuan_context_t ctx, char *line)
+
+ static const char hlp_readkey[] =
+ "READKEY <hexstring_with_keygrip>\n"
++ " --card <keyid>\n"
+ "\n"
+- "Return the public key for the given keygrip.";
++ "Return the public key for the given keygrip or keyid.\n"
++ "With --card, private key file with card information will be created.";
+ static gpg_error_t
+ cmd_readkey (assuan_context_t ctx, char *line)
+ {
+@@ -990,10 +992,57 @@ cmd_readkey (assuan_context_t ctx, char *line)
+ int rc;
+ unsigned char grip[20];
+ gcry_sexp_t s_pkey = NULL;
++ unsigned char *pkbuf = NULL;
++ size_t pkbuflen;
+
+ if (ctrl->restricted)
+ return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
++ if (has_option_name (line, "--card"))
++ {
++ const char *keyid;
++ char *serialno = NULL;
++
++ keyid = skip_options (line);
++
++ rc = agent_card_getattr (ctrl, "SERIALNO", &serialno);
++ if (rc)
++ {
++ log_error (_("error getting serial number of card: %s\n"),
++ gpg_strerror (rc));
++ goto leave;
++ }
++
++ pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
++ rc = agent_card_readkey (ctrl, keyid, &pkbuf);
++ if (rc)
++ goto leave;
++ rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)pkbuf, pkbuflen);
++ if (rc)
++ goto leave;
++
++ if (!gcry_pk_get_keygrip (s_pkey, grip))
++ {
++ rc = gcry_pk_testkey (s_pkey);
++ if (rc == 0)
++ rc = gpg_error (GPG_ERR_INTERNAL);
++
++ goto leave;
++ }
++
++ rc = agent_write_shadow_key (grip, serialno, keyid, pkbuf, 0);
++ if (rc)
++ goto leave;
++
++ rc = assuan_send_data (ctx, pkbuf, pkbuflen);
++
++ leave:
++ xfree (serialno);
++ xfree (pkbuf);
++ gcry_sexp_release (s_pkey);
++ return leave_cmd (ctx, rc);
++ }
++
+ rc = parse_keygrip (ctx, line, grip);
+ if (rc)
+ return rc; /* Return immediately as this is already an Assuan error code.*/
+@@ -1001,20 +1050,16 @@ cmd_readkey (assuan_context_t ctx, char *line)
+ rc = agent_public_key_from_file (ctrl, grip, &s_pkey);
+ if (!rc)
+ {
+- size_t len;
+- unsigned char *buf;
+-
+- len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+- assert (len);
+- buf = xtrymalloc (len);
+- if (!buf)
++ pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
++ assert (pkbuflen);
++ pkbuf = xtrymalloc (pkbuflen);
++ if (!pkbuf)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+- len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, buf, len);
+- assert (len);
+- rc = assuan_send_data (ctx, buf, len);
+- xfree (buf);
++ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen);
++ rc = assuan_send_data (ctx, pkbuf, pkbuflen);
++ xfree (pkbuf);
+ }
+ gcry_sexp_release (s_pkey);
+ }
+diff --git a/agent/findkey.c b/agent/findkey.c
+index c5ab0e9..23e94f0 100644
+--- a/agent/findkey.c
++++ b/agent/findkey.c
+@@ -1492,3 +1492,39 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text,
+ gcry_sexp_release (s_skey);
+ return err;
+ }
++
++
++/* Write an S-expression formatted shadow key to our key storage.
++ Shadow key is created by an S-expression public key in PKBUF and
++ card's SERIALNO and the IDSTRING. With FORCE passed as true an
++ existing key with the given GRIP will get overwritten. */
++gpg_error_t
++agent_write_shadow_key (const unsigned char *grip,
++ const char *serialno, const char *keyid,
++ const unsigned char *pkbuf, int force)
++{
++ gpg_error_t err;
++ unsigned char *shadow_info;
++ unsigned char *shdkey;
++ size_t len;
++
++ shadow_info = make_shadow_info (serialno, keyid);
++ if (!shadow_info)
++ return gpg_error_from_syserror ();
++
++ err = agent_shadow_key (pkbuf, shadow_info, &shdkey);
++ xfree (shadow_info);
++ if (err)
++ {
++ log_error ("shadowing the key failed: %s\n", gpg_strerror (err));
++ return err;
++ }
++
++ len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
++ err = agent_write_private_key (grip, shdkey, len, force);
++ xfree (shdkey);
++ if (err)
++ log_error ("error writing key: %s\n", gpg_strerror (err));
++
++ return err;
++}
+diff --git a/agent/learncard.c b/agent/learncard.c
+index e9304fb..103a821 100644
+--- a/agent/learncard.c
++++ b/agent/learncard.c
+@@ -381,8 +381,7 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
+
+ for (item = parm.info; item; item = item->next)
+ {
+- unsigned char *pubkey, *shdkey;
+- size_t n;
++ unsigned char *pubkey;
+
+ if (opt.verbose)
+ log_info (" id: %s (grip=%s)\n", item->id, item->hexgrip);
+@@ -410,33 +409,10 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
+ goto leave;
+ }
+
+- {
+- unsigned char *shadow_info = make_shadow_info (serialno, item->id);
+- if (!shadow_info)
+- {
+- rc = gpg_error (GPG_ERR_ENOMEM);
+- xfree (pubkey);
+- goto leave;
+- }
+- rc = agent_shadow_key (pubkey, shadow_info, &shdkey);
+- xfree (shadow_info);
+- }
++ rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force);
+ xfree (pubkey);
+ if (rc)
+- {
+- log_error ("shadowing the key failed: %s\n", gpg_strerror (rc));
+- goto leave;
+- }
+- n = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
+- assert (n);
+-
+- rc = agent_write_private_key (grip, shdkey, n, force);
+- xfree (shdkey);
+- if (rc)
+- {
+- log_error ("error writing key: %s\n", gpg_strerror (rc));
+- goto leave;
+- }
++ goto leave;
+
+ if (opt.verbose)
+ log_info (" id: %s - shadow key created\n", item->id);
diff --git a/debian/patches/0079-g10-smartcard-keygen-change.patch b/debian/patches/0079-g10-smartcard-keygen-change.patch
new file mode 100644
index 0000000..bd028b3
--- /dev/null
+++ b/debian/patches/0079-g10-smartcard-keygen-change.patch
@@ -0,0 +1,341 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Thu, 20 Oct 2016 13:30:47 +0900
+Subject: g10: smartcard keygen change.
+
+* g10/call-agent.c (scd_genkey_cb_append_savedbytes): Remove.
+(scd_genkey_cb): Only handle KEY-CREATED-AT and PROGRESS.
+(agent_scd_genkey): Remove INFO argument. CREATETIME is now in/out
+argument.
+(agent_readkey): Use READKEY --card instead of SCD READKEY.
+* g10/keygen.c (gen_card_key): Use READKEY --card command of the agent
+to retrieve public key information from card and let the agent make
+a file for private key with shadow info.
+--
+
+This change removes gpg's KEY-DATA handling for SCD GENKEY. Information
+with KEY-DATA is simply not used. Instead, it is read by READKEY --card
+command of gpg-agent. This can consolidate public key handling in a
+single method by READKEY.
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ g10/call-agent.c | 118 ++++++-------------------------------------------------
+ g10/call-agent.h | 10 +----
+ g10/keygen.c | 54 ++++++++++++++-----------
+ 3 files changed, 45 insertions(+), 137 deletions(-)
+
+diff --git a/g10/call-agent.c b/g10/call-agent.c
+index 0fb392c..632cabe 100644
+--- a/g10/call-agent.c
++++ b/g10/call-agent.c
+@@ -103,13 +103,6 @@ struct cache_nonce_parm_s
+ };
+
+
+-struct scd_genkey_parm_s
+-{
+- struct agent_card_genkey_s *cgk;
+- char *savedbytes; /* Malloced space to save key parameter chunks. */
+-};
+-
+-
+ static gpg_error_t learn_status_cb (void *opaque, const char *line);
+
+
+@@ -979,133 +972,50 @@ agent_scd_writekey (int keyno, const char *serialno,
+
+
+
+-static gpg_error_t
+-scd_genkey_cb_append_savedbytes (struct scd_genkey_parm_s *parm,
+- const char *line)
+-{
+- gpg_error_t err = 0;
+- char *p;
+-
+- if (!parm->savedbytes)
+- {
+- parm->savedbytes = xtrystrdup (line);
+- if (!parm->savedbytes)
+- err = gpg_error_from_syserror ();
+- }
+- else
+- {
+- p = xtrymalloc (strlen (parm->savedbytes) + strlen (line) + 1);
+- if (!p)
+- err = gpg_error_from_syserror ();
+- else
+- {
+- strcpy (stpcpy (p, parm->savedbytes), line);
+- xfree (parm->savedbytes);
+- parm->savedbytes = p;
+- }
+- }
+-
+- return err;
+-}
+-
+ /* Status callback for the SCD GENKEY command. */
+ static gpg_error_t
+ scd_genkey_cb (void *opaque, const char *line)
+ {
+- struct scd_genkey_parm_s *parm = opaque;
++ u32 *createtime = opaque;
+ const char *keyword = line;
+ int keywordlen;
+- gpg_error_t rc = 0;
+
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ while (spacep (line))
+ line++;
+
+- if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
+- {
+- parm->cgk->fprvalid = unhexify_fpr (line, parm->cgk->fpr);
+- }
+- else if (keywordlen == 8 && !memcmp (keyword, "KEY-DATA", keywordlen))
+- {
+- gcry_mpi_t a;
+- const char *name = line;
+-
+- while (*line && !spacep (line))
+- line++;
+- while (spacep (line))
+- line++;
+-
+- if (*name == '-' && spacep (name+1))
+- rc = scd_genkey_cb_append_savedbytes (parm, line);
+- else
+- {
+- if (parm->savedbytes)
+- {
+- rc = scd_genkey_cb_append_savedbytes (parm, line);
+- if (!rc)
+- rc = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX,
+- parm->savedbytes, 0, NULL);
+- }
+- else
+- rc = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0, NULL);
+- if (rc)
+- log_error ("error parsing received key data: %s\n",
+- gpg_strerror (rc));
+- else if (*name == 'n' && spacep (name+1))
+- parm->cgk->n = a;
+- else if (*name == 'e' && spacep (name+1))
+- parm->cgk->e = a;
+- else
+- {
+- log_info ("unknown parameter name in received key data\n");
+- gcry_mpi_release (a);
+- rc = gpg_error (GPG_ERR_INV_PARAMETER);
+- }
+-
+- xfree (parm->savedbytes);
+- parm->savedbytes = NULL;
+- }
+- }
+- else if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen))
++ if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen))
+ {
+- parm->cgk->created_at = (u32)strtoul (line, NULL, 10);
++ *createtime = (u32)strtoul (line, NULL, 10);
+ }
+ else if (keywordlen == 8 && !memcmp (keyword, "PROGRESS", keywordlen))
+ {
+ write_status_text (STATUS_PROGRESS, line);
+ }
+
+- return rc;
++ return 0;
+ }
+
+-/* Send a GENKEY command to the SCdaemon. SERIALNO is not used in
+- this implementation. If CREATEDATE is not 0, it will be passed to
+- SCDAEMON so that the key is created with this timestamp. INFO will
+- receive information about the generated key. */
++/* Send a GENKEY command to the SCdaemon. If CREATETIME is not 0, it
++ will be passed to SCDAEMON so that the key is created with this
++ timestamp. On success, creation time is stored back to CREATETIME. */
+ int
+-agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
+- const char *serialno, u32 createtime)
++agent_scd_genkey (int keyno, int force, u32 *createtime)
+ {
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+ gnupg_isotime_t tbuf;
+- struct scd_genkey_parm_s parms;
+ struct default_inq_parm_s dfltparm;
+
+ memset (&dfltparm, 0, sizeof dfltparm);
+
+- (void)serialno;
+-
+- memset (&parms, 0, sizeof parms);
+- parms.cgk = info;
+-
+ rc = start_agent (NULL, 1);
+ if (rc)
+ return rc;
+
+- if (createtime)
+- epoch2isotime (tbuf, createtime);
++ if (*createtime)
++ epoch2isotime (tbuf, *createtime);
+ else
+ *tbuf = 0;
+
+@@ -1116,12 +1026,9 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
+ line[DIM(line)-1] = 0;
+
+ dfltparm.ctx = agent_ctx;
+- memset (info, 0, sizeof *info);
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL, default_inq_cb, &dfltparm,
+- scd_genkey_cb, &parms);
+-
+- xfree (parms.savedbytes);
++ scd_genkey_cb, createtime);
+
+ status_sc_op_failure (rc);
+ return rc;
+@@ -1854,7 +1761,8 @@ agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+ if (err)
+ return err;
+
+- snprintf (line, DIM(line)-1, "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip);
++ snprintf (line, DIM(line)-1, "READKEY %s%s", fromcard? "--card ":"",
++ hexkeygrip);
+
+ init_membuf (&data, 1024);
+ err = assuan_transact (agent_ctx, line,
+diff --git a/g10/call-agent.h b/g10/call-agent.h
+index d85a6fd..032c345 100644
+--- a/g10/call-agent.h
++++ b/g10/call-agent.h
+@@ -68,13 +68,6 @@ struct agent_card_info_s
+ unsigned int status_indicator;
+ };
+
+-struct agent_card_genkey_s {
+- char fprvalid;
+- char fpr[20];
+- u32 created_at;
+- gcry_mpi_t n;
+- gcry_mpi_t e;
+-};
+
+
+ /* Release the card info structure. */
+@@ -107,8 +100,7 @@ int agent_scd_writekey (int keyno, const char *serialno,
+ 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,
+- const char *serialno, u32 createtime);
++int agent_scd_genkey (int keyno, int force, u32 *createtime);
+
+ /* Send a READKEY command to the SCdaemon. */
+ int agent_scd_readcert (const char *certidstr,
+diff --git a/g10/keygen.c b/g10/keygen.c
+index 9cf314d..90f8544 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -4870,9 +4870,14 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+ {
+ #ifdef ENABLE_CARD_SUPPORT
+ gpg_error_t err;
+- struct agent_card_genkey_s info;
+ PACKET *pkt;
+ PKT_public_key *pk;
++ char keyid[10];
++ unsigned char *public;
++ gcry_sexp_t s_key;
++
++ snprintf (keyid, DIM(keyid)-1, "OPENPGP.%d", keyno);
++ keyid[DIM(keyid)-1] = 0;
+
+ if (algo != PUBKEY_ALGO_RSA)
+ return gpg_error (GPG_ERR_PUBKEY_ALGO);
+@@ -4888,7 +4893,7 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+ }
+
+ /* Note: SCD knows the serialnumber, thus there is no point in passing it. */
+- err = agent_scd_genkey (&info, keyno, 1, NULL, *timestamp);
++ err = agent_scd_genkey (keyno, 1, timestamp);
+ /* The code below is not used because we force creation of
+ * the a card key (3rd arg).
+ * if (gpg_err_code (rc) == GPG_ERR_EEXIST)
+@@ -4898,16 +4903,9 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+ * tty_printf ("\n");
+ * if ( cpr_get_answer_is_yes( "keygen.card.replace_key",
+ * _("Replace existing key? ")))
+- * rc = agent_scd_genkey (&info, keyno, 1);
++ * rc = agent_scd_genkey (keyno, 1, timestamp);
+ * }
+ */
+- if (!err && (!info.n || !info.e))
+- {
+- log_error ("communication error with SCD\n");
+- gcry_mpi_release (info.n);
+- gcry_mpi_release (info.e);
+- err = gpg_error (GPG_ERR_GENERAL);
+- }
+ if (err)
+ {
+ log_error ("key generation failed: %s\n", gpg_strerror (err));
+@@ -4916,30 +4914,40 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+ return err;
+ }
+
+- /* Send the learn command so that the agent creates a shadow key for
++ /* Send the READKEY command so that the agent creates a shadow key for
+ card key. We need to do that now so that we are able to create
+ the self-signatures. */
+- err = agent_scd_learn (NULL, 0);
++ err = agent_readkey (NULL, 1, keyid, &public);
++ if (err)
++ return err;
++ err = gcry_sexp_sscan (&s_key, NULL, public,
++ gcry_sexp_canon_len (public, 0, NULL, NULL));
++ xfree (public);
++ if (err)
++ return err;
++
++ if (algo == PUBKEY_ALGO_RSA)
++ err = key_from_sexp (pk->pkey, s_key, "public-key", "ne");
++ else if (algo == PUBKEY_ALGO_ECDSA
++ || algo == PUBKEY_ALGO_EDDSA
++ || algo == PUBKEY_ALGO_ECDH )
++ err = ecckey_from_sexp (pk->pkey, s_key, algo);
++ else
++ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
++ gcry_sexp_release (s_key);
++
+ if (err)
+ {
+- /* Oops: Card removed during generation. */
+- log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
+- xfree (pkt);
+- xfree (pk);
++ log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) );
++ free_public_key (pk);
+ return err;
+ }
+
+- if (*timestamp != info.created_at)
+- log_info ("NOTE: the key does not use the suggested creation date\n");
+- *timestamp = info.created_at;
+-
+- pk->timestamp = info.created_at;
++ pk->timestamp = *timestamp;
+ pk->version = 4;
+ if (expireval)
+ pk->expiredate = pk->timestamp + expireval;
+ pk->pubkey_algo = algo;
+- pk->pkey[0] = info.n;
+- pk->pkey[1] = info.e;
+
+ pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY;
+ pkt->pkt.public_key = pk;
diff --git a/debian/patches/0080-scd-GENKEY-updates-the-public-key-in-APP.patch b/debian/patches/0080-scd-GENKEY-updates-the-public-key-in-APP.patch
new file mode 100644
index 0000000..099c03a
--- /dev/null
+++ b/debian/patches/0080-scd-GENKEY-updates-the-public-key-in-APP.patch
@@ -0,0 +1,568 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Thu, 20 Oct 2016 16:25:47 +0900
+Subject: scd: GENKEY updates the public key in APP.
+
+* scd/app-openpgp.c (rsa_read_pubkey, ecc_read_pubkey): New.
+(read_public_key): New.
+(get_public_key, do_genkey): Use read_public_key.
+
+--
+
+With this change, since GENKEY updates the public key (pk[keyno].key) in
+APP, READKEY will be possible after the command even for the old
+card (version <= 0x0100).
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ scd/app-openpgp.c | 485 +++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 257 insertions(+), 228 deletions(-)
+
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index 09e4800..843fdf0 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -1221,6 +1221,246 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
+ #endif /*GNUPG_MAJOR_VERSION > 1*/
+
+
++static gpg_error_t
++rsa_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
++ const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
++{
++ gpg_error_t err;
++ const unsigned char *m, *e;
++ size_t mlen, elen;
++ unsigned char *mbuf = NULL, *ebuf = NULL;
++
++ m = find_tlv (data, datalen, 0x0081, &mlen);
++ if (!m)
++ {
++ log_error (_("response does not contain the RSA modulus\n"));
++ return gpg_error (GPG_ERR_CARD);
++ }
++
++ e = find_tlv (data, datalen, 0x0082, &elen);
++ if (!e)
++ {
++ log_error (_("response does not contain the RSA public exponent\n"));
++ return gpg_error (GPG_ERR_CARD);
++ }
++
++ if (ctrl)
++ {
++ send_key_data (ctrl, "n", m, mlen);
++ send_key_data (ctrl, "e", e, elen);
++ }
++
++ for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
++ ;
++ for (; elen && !*e; elen--, e++) /* strip leading zeroes */
++ ;
++
++ if (ctrl)
++ {
++ unsigned char fprbuf[20];
++
++ err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
++ m, mlen, e, elen);
++ if (err)
++ return err;
++
++ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
++ }
++
++ mbuf = xtrymalloc (mlen + 1);
++ if (!mbuf)
++ {
++ err = gpg_error_from_syserror ();
++ goto leave;
++ }
++ /* Prepend numbers with a 0 if needed. */
++ if (mlen && (*m & 0x80))
++ {
++ *mbuf = 0;
++ memcpy (mbuf+1, m, mlen);
++ mlen++;
++ }
++ else
++ memcpy (mbuf, m, mlen);
++
++ ebuf = xtrymalloc (elen + 1);
++ if (!ebuf)
++ {
++ err = gpg_error_from_syserror ();
++ goto leave;
++ }
++ /* Prepend numbers with a 0 if needed. */
++ if (elen && (*e & 0x80))
++ {
++ *ebuf = 0;
++ memcpy (ebuf+1, e, elen);
++ elen++;
++ }
++ else
++ memcpy (ebuf, e, elen);
++
++ err = gcry_sexp_build (r_sexp, NULL, "(public-key(rsa(n%b)(e%b)))",
++ (int)mlen, mbuf, (int)elen, ebuf);
++ leave:
++ xfree (mbuf);
++ xfree (ebuf);
++ return err;
++}
++
++static gpg_error_t
++ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
++ const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
++{
++ gpg_error_t err;
++ unsigned char *qbuf;
++ const unsigned char *ecc_q;
++ size_t ecc_q_len;
++ gcry_mpi_t oid;
++ int n;
++ const unsigned char *oidbuf;
++ size_t oid_len;
++ int algo;
++ const char *format;
++ const char *curve;
++
++ ecc_q = find_tlv (data, datalen, 0x0086, &ecc_q_len);
++ if (!ecc_q)
++ {
++ log_error (_("response does not contain the EC public key\n"));
++ return gpg_error (GPG_ERR_CARD);
++ }
++
++ err = openpgp_oid_from_str (app->app_local->keyattr[keyno].ecc.oid, &oid);
++ if (err)
++ return err;
++
++ oidbuf = gcry_mpi_get_opaque (oid, &n);
++ if (!oidbuf)
++ {
++ err = gpg_error_from_syserror ();
++ gcry_mpi_release (oid);
++ return err;
++ }
++ gcry_mpi_release (oid);
++ oid_len = (n+7)/8;
++
++ qbuf = xtrymalloc (ecc_q_len + 1);
++ if (!qbuf)
++ return gpg_error_from_syserror ();
++
++ if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
++ { /* Prepend 0x40 prefix. */
++ *qbuf = 0x40;
++ memcpy (qbuf+1, ecc_q, ecc_q_len);
++ ecc_q_len++;
++ }
++ else
++ memcpy (qbuf, ecc_q, ecc_q_len);
++
++ if (ctrl)
++ {
++ send_key_data (ctrl, "q", ecc_q, ecc_q_len);
++ send_key_data (ctrl, "curve", oidbuf, oid_len);
++ }
++
++ if (keyno == 1)
++ {
++ if (ctrl)
++ send_key_data (ctrl, "kdf", "\x03\x01\x08\x07", (size_t)4);
++ algo = PUBKEY_ALGO_ECDH;
++ }
++ else
++ {
++ if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
++ algo = PUBKEY_ALGO_EDDSA;
++ else
++ algo = PUBKEY_ALGO_ECDSA;
++ }
++
++ if (ctrl)
++ {
++ unsigned char fprbuf[20];
++
++ err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
++ qbuf, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
++ if (err)
++ goto leave;
++
++ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
++ }
++
++ if (!(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
++ format = "(public-key(ecc(curve%s)(q%b)))";
++ else if (keyno == 1)
++ format = "(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))";
++ else
++ format = "(public-key(ecc(curve%s)(flags eddsa)(q%b)))";
++
++ curve = openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 1);
++ err = gcry_sexp_build (r_sexp, NULL, format, curve, (int)ecc_q_len, qbuf);
++ leave:
++ xfree (qbuf);
++ return err;
++}
++
++
++/* Parse tag-length-value data for public key in BUFFER of BUFLEN
++ length. Key of KEYNO in APP is updated with an S-expression of
++ public key. When CTRL is not NULL, fingerprint is computed with
++ CREATED_AT, and fingerprint is written to the card, and key data
++ and fingerprint are send back to the client side.
++ */
++static gpg_error_t
++read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
++ const unsigned char *buffer, size_t buflen)
++{
++ gpg_error_t err;
++ const unsigned char *data;
++ size_t datalen;
++ gcry_sexp_t s_pkey = NULL;
++
++ data = find_tlv (buffer, buflen, 0x7F49, &datalen);
++ if (!data)
++ {
++ log_error (_("response does not contain the public key data\n"));
++ return gpg_error (GPG_ERR_CARD);
++ }
++
++ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
++ err = rsa_read_pubkey (app, ctrl, created_at, keyno,
++ data, datalen, &s_pkey);
++ else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
++ err = ecc_read_pubkey (app, ctrl, created_at, keyno,
++ data, datalen, &s_pkey);
++ else
++ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
++
++ if (!err)
++ {
++ unsigned char *keybuf;
++ size_t len;
++
++ len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
++ keybuf = xtrymalloc (len);
++ if (!data)
++ {
++ err = gpg_error_from_syserror ();
++ gcry_sexp_release (s_pkey);
++ return err;
++ }
++
++ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
++ gcry_sexp_release (s_pkey);
++
++ app->app_local->pk[keyno].key = keybuf;
++ /* Decrement for trailing '\0' */
++ app->app_local->pk[keyno].keylen = len - 1;
++ }
++
++ return err;
++}
++
++
+ /* Get the public key for KEYNO and store it as an S-expresion with
+ the APP handle. On error that field gets cleared. If we already
+ know about the public key we will just return. Note that this does
+@@ -1237,12 +1477,10 @@ get_public_key (app_t app, int keyno)
+ {
+ gpg_error_t err = 0;
+ unsigned char *buffer;
+- const unsigned char *keydata, *m, *e;
+- size_t buflen, keydatalen;
++ const unsigned char *m, *e;
++ size_t buflen;
+ size_t mlen = 0;
+ size_t elen = 0;
+- unsigned char *mbuf = NULL;
+- unsigned char *ebuf = NULL;
+ char *keybuf = NULL;
+ gcry_sexp_t s_pkey;
+ size_t len;
+@@ -1286,42 +1524,7 @@ get_public_key (app_t app, int keyno)
+ goto leave;
+ }
+
+- keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
+- if (!keydata)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the public key data\n"));
+- goto leave;
+- }
+-
+- if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+- {
+- m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+- if (!m)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the RSA modulus\n"));
+- goto leave;
+- }
+-
+- e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+- if (!e)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the RSA public exponent\n"));
+- goto leave;
+- }
+- }
+- else
+- {
+- m = find_tlv (keydata, keydatalen, 0x0086, &mlen);
+- if (!m)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the EC public key\n"));
+- goto leave;
+- }
+- }
++ err = read_public_key (app, NULL, 0U, keyno, buffer, buflen);
+ }
+ else
+ {
+@@ -1375,98 +1578,35 @@ get_public_key (app_t app, int keyno)
+ gpg_strerror (err));
+ goto leave;
+ }
+- }
+
+- mbuf = xtrymalloc (mlen + 1);
+- if (!mbuf)
+- {
+- err = gpg_error_from_syserror ();
+- goto leave;
+- }
++ err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
++ (int)mlen, m, (int)elen, e);
++ if (err)
++ goto leave;
+
+- if ((app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA
+- || (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC
+- && !(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)))
+- && mlen && (*m & 0x80))
+- { /* Prepend numbers with a 0 if needed for MPI. */
+- *mbuf = 0;
+- memcpy (mbuf+1, m, mlen);
+- mlen++;
+- }
+- else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC
+- && (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+- { /* Prepend 0x40 prefix. */
+- *mbuf = 0x40;
+- memcpy (mbuf+1, m, mlen);
+- mlen++;
+- }
+- else
+- memcpy (mbuf, m, mlen);
++ len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+
+- if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+- {
+- ebuf = xtrymalloc (elen + 1);
+- if (!ebuf)
++ keybuf = xtrymalloc (len);
++ if (!keybuf)
+ {
+ err = gpg_error_from_syserror ();
++ gcry_sexp_release (s_pkey);
+ goto leave;
+ }
+- /* Prepend numbers with a 0 if needed. */
+- if (elen && (*e & 0x80))
+- {
+- *ebuf = 0;
+- memcpy (ebuf+1, e, elen);
+- elen++;
+- }
+- else
+- memcpy (ebuf, e, elen);
+
+- err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
+- (int)mlen, mbuf, (int)elen, ebuf);
+- }
+- else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
+- {
+- char *format;
+-
+- if (!(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+- format = "(public-key(ecc(curve%s)(q%b)))";
+- else if (keyno == 1)
+- format = "(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))";
+- else
+- format = "(public-key(ecc(curve%s)(flags eddsa)(q%b)))";
+-
+- err = gcry_sexp_build (&s_pkey, NULL, format,
+- openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 1),
+- (int)mlen, mbuf);
+- }
+- else
+- err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+-
+- if (err)
+- goto leave;
+-
+- len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+-
+- keybuf = xtrymalloc (len);
+- if (!keybuf)
+- {
++ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+ gcry_sexp_release (s_pkey);
+- err = gpg_error_from_syserror ();
+- goto leave;
+- }
+- gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+- gcry_sexp_release (s_pkey);
+
+- app->app_local->pk[keyno].key = (unsigned char*)keybuf;
+- app->app_local->pk[keyno].keylen = len - 1; /* Decrement for trailing '\0' */
++ app->app_local->pk[keyno].key = (unsigned char*)keybuf;
++ /* Decrement for trailing '\0' */
++ app->app_local->pk[keyno].keylen = len - 1;
++ }
+
+ leave:
+ /* Set a flag to indicate that we tried to read the key. */
+ app->app_local->pk[keyno].read_done = 1;
+
+ xfree (buffer);
+- xfree (mbuf);
+- xfree (ebuf);
+ return err;
+ }
+ #endif /* GNUPG_MAJOR_VERSION > 1 */
+@@ -3533,7 +3673,6 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ {
+ gpg_error_t err;
+ char numbuf[30];
+- unsigned char fprbuf[20];
+ unsigned char *buffer = NULL;
+ const unsigned char *keydata;
+ size_t buflen, keydatalen;
+@@ -3623,117 +3762,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ send_status_info (ctrl, "KEY-CREATED-AT",
+ numbuf, (size_t)strlen(numbuf), NULL, 0);
+
+- if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+- {
+- const unsigned char *m, *e;
+- size_t mlen, elen;
+-
+- m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+- if (!m)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the RSA modulus\n"));
+- goto leave;
+- }
+- /* log_printhex ("RSA n:", m, mlen); */
+- send_key_data (ctrl, "n", m, mlen);
+-
+- e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+- if (!e)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the RSA public exponent\n"));
+- goto leave;
+- }
+- /* log_printhex ("RSA e:", e, elen); */
+- send_key_data (ctrl, "e", e, elen);
+-
+- for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+- ;
+- for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+- ;
+-
+- err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
+- m, mlen, e, elen);
+- }
+- else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
+- {
+- const unsigned char *ecc_q;
+- size_t ecc_q_len;
+- gcry_mpi_t oid;
+- int n;
+- const unsigned char *oidbuf;
+- size_t oid_len;
+- int algo;
+-
+- ecc_q = find_tlv (keydata, keydatalen, 0x0086, &ecc_q_len);
+- if (!ecc_q)
+- {
+- err = gpg_error (GPG_ERR_CARD);
+- log_error (_("response does not contain the EC public key\n"));
+- goto leave;
+- }
+-
+- err = openpgp_oid_from_str (app->app_local->keyattr[keyno].ecc.oid, &oid);
+- if (err)
+- goto leave;
+-
+- oidbuf = gcry_mpi_get_opaque (oid, &n);
+- if (!oidbuf)
+- {
+- err = gpg_error_from_syserror ();
+- gcry_mpi_release (oid);
+- goto leave;
+- }
+- gcry_mpi_release (oid);
+- oid_len = (n+7)/8;
+-
+- if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+- { /* Prepend 0x40 prefix. */
+- unsigned char *q = xtrymalloc (ecc_q_len + 1);
+-
+- if (!q)
+- {
+- err = gpg_error_from_syserror ();
+- goto leave;
+- }
+- *q = 0x40;
+- memcpy (q+1, ecc_q, ecc_q_len);
+- send_key_data (ctrl, "q", q, ecc_q_len + 1);
+- xfree (q);
+- }
+- else
+- {
+- /* strip leading zeroes */
+- for (; ecc_q_len && !*ecc_q; ecc_q_len--, ecc_q++)
+- ;
+- send_key_data (ctrl, "q", ecc_q, ecc_q_len);
+- }
+-
+- send_key_data (ctrl, "curve", oidbuf, oid_len);
+-
+- if (keyno == 1)
+- {
+- send_key_data (ctrl, "kdf", "\x03\x01\x08\x07", (size_t)4);
+- algo = PUBKEY_ALGO_ECDH;
+- }
+- else
+- {
+- if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+- algo = PUBKEY_ALGO_EDDSA;
+- else
+- algo = PUBKEY_ALGO_ECDSA;
+- }
+-
+- err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+- ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
+- }
+-
+- if (err)
+- goto leave;
+- send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+-
+-
++ err = read_public_key (app, ctrl, created_at, keyno, buffer, buflen);
+ leave:
+ xfree (buffer);
+ return err;
diff --git a/debian/patches/0081-agent-g10-Fix-keygen.patch b/debian/patches/0081-agent-g10-Fix-keygen.patch
new file mode 100644
index 0000000..d72da6c
--- /dev/null
+++ b/debian/patches/0081-agent-g10-Fix-keygen.patch
@@ -0,0 +1,44 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Thu, 20 Oct 2016 20:01:46 +0900
+Subject: agent, g10: Fix keygen.
+
+* agent/command.c (cmd_readkey): Get length after card_readkey.
+* g10/keygen.c (gen_card_key): Fix off-by-one error.
+
+--
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ agent/command.c | 2 +-
+ g10/keygen.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/agent/command.c b/agent/command.c
+index 12d90ed..1cab1d4 100644
+--- a/agent/command.c
++++ b/agent/command.c
+@@ -1013,10 +1013,10 @@ cmd_readkey (assuan_context_t ctx, char *line)
+ goto leave;
+ }
+
+- pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
+ rc = agent_card_readkey (ctrl, keyid, &pkbuf);
+ if (rc)
+ goto leave;
++ pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
+ rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)pkbuf, pkbuflen);
+ if (rc)
+ goto leave;
+diff --git a/g10/keygen.c b/g10/keygen.c
+index 90f8544..2115b5a 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -4876,7 +4876,7 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+ unsigned char *public;
+ gcry_sexp_t s_key;
+
+- snprintf (keyid, DIM(keyid)-1, "OPENPGP.%d", keyno);
++ snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno);
+ keyid[DIM(keyid)-1] = 0;
+
+ if (algo != PUBKEY_ALGO_RSA)
diff --git a/debian/patches/0082-agent-Fix-saving-with-FORCE-1.patch b/debian/patches/0082-agent-Fix-saving-with-FORCE-1.patch
new file mode 100644
index 0000000..5f39692
--- /dev/null
+++ b/debian/patches/0082-agent-Fix-saving-with-FORCE-1.patch
@@ -0,0 +1,54 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Fri, 21 Oct 2016 10:57:29 +0900
+Subject: agent: Fix saving with FORCE=1.
+
+* agent/findkey.c (agent_write_private_key): Recover from an error of
+GPG_ERR_ENOENT when FORCE=1 and it is opened with "rb+".
+
+--
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ agent/findkey.c | 25 +++++++++++++++++++------
+ 1 file changed, 19 insertions(+), 6 deletions(-)
+
+diff --git a/agent/findkey.c b/agent/findkey.c
+index 23e94f0..162e8c2 100644
+--- a/agent/findkey.c
++++ b/agent/findkey.c
+@@ -152,17 +152,30 @@ agent_write_private_key (const unsigned char *grip,
+ if (!fp)
+ {
+ gpg_error_t tmperr = gpg_error_from_syserror ();
+- log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr));
+- xfree (fname);
+- return tmperr;
+- }
+
+- /* See if an existing key is in extended format. */
+- if (force)
++ if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT)
++ {
++ fp = es_fopen (fname, "wbx,mode=-rw");
++ if (!fp)
++ {
++ tmperr = gpg_error_from_syserror ();
++ goto error;
++ }
++ }
++ else
++ {
++ error:
++ log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr));
++ xfree (fname);
++ return tmperr;
++ }
++ }
++ else if (force)
+ {
+ gpg_error_t rc;
+ char first;
+
++ /* See if an existing key is in extended format. */
+ if (es_fread (&first, 1, 1, fp) != 1)
+ {
+ rc = gpg_error_from_syserror ();
diff --git a/debian/patches/0083-Fix-use-cases-of-snprintf.patch b/debian/patches/0083-Fix-use-cases-of-snprintf.patch
new file mode 100644
index 0000000..3960058
--- /dev/null
+++ b/debian/patches/0083-Fix-use-cases-of-snprintf.patch
@@ -0,0 +1,999 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Fri, 21 Oct 2016 12:04:46 +0900
+Subject: Fix use cases of snprintf.
+
+* agent/call-pinentry.c, agent/call-scd.c, agent/command.c,
+build-aux/speedo/w32/g4wihelp.c, common/get-passphrase.c,
+dirmngr/dirmngr.c, g10/call-agent.c, g10/cpr.c, g10/keygen.c,
+g10/openfile.c, g10/passphrase.c, scd/app-openpgp.c, scd/scdaemon.c,
+sm/call-agent.c, sm/call-dirmngr.c, sm/certreqgen.c: Fix assuming C99.
+
+--
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ agent/call-pinentry.c | 75 +++++++++++++++--------------------------
+ agent/call-scd.c | 12 +++----
+ agent/command.c | 2 +-
+ build-aux/speedo/w32/g4wihelp.c | 8 ++---
+ common/get-passphrase.c | 6 ++--
+ dirmngr/dirmngr.c | 3 +-
+ g10/call-agent.c | 65 ++++++++++++++---------------------
+ g10/cpr.c | 6 ++--
+ g10/keygen.c | 1 -
+ g10/openfile.c | 4 +--
+ g10/passphrase.c | 4 +--
+ scd/app-openpgp.c | 2 +-
+ scd/scdaemon.c | 3 +-
+ sm/call-agent.c | 50 ++++++++++-----------------
+ sm/call-dirmngr.c | 11 +++---
+ sm/certreqgen.c | 2 +-
+ 16 files changed, 97 insertions(+), 157 deletions(-)
+
+diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c
+index 0f24086..46db9e8 100644
+--- a/agent/call-pinentry.c
++++ b/agent/call-pinentry.c
+@@ -734,8 +734,7 @@ setup_qualitybar (ctrl_t ctrl)
+ /* TRANSLATORS: This string is displayed by Pinentry as the label
+ for the quality bar. */
+ tmpstr = try_percent_escape (L_("Quality:"), "\t\r\n\f\v");
+- snprintf (line, DIM(line)-1, "SETQUALITYBAR %s", tmpstr? tmpstr:"");
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETQUALITYBAR %s", tmpstr? tmpstr:"");
+ xfree (tmpstr);
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc == 103 /*(Old assuan error code)*/
+@@ -763,8 +762,7 @@ setup_qualitybar (ctrl_t ctrl)
+ }
+ tmpstr = try_percent_escape (tooltip, "\t\r\n\f\v");
+ xfree (tmpstr2);
+- snprintf (line, DIM(line)-1, "SETQUALITYBAR_TT %s", tmpstr? tmpstr:"");
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETQUALITYBAR_TT %s", tmpstr? tmpstr:"");
+ xfree (tmpstr);
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc == 103 /*(Old assuan error code)*/
+@@ -887,27 +885,25 @@ agent_askpin (ctrl_t ctrl,
+ if (keyinfo && (cache_mode == CACHE_MODE_NORMAL
+ || cache_mode == CACHE_MODE_USER
+ || cache_mode == CACHE_MODE_SSH))
+- snprintf (line, DIM(line)-1, "SETKEYINFO %c/%s",
++ snprintf (line, DIM(line), "SETKEYINFO %c/%s",
+ cache_mode == CACHE_MODE_USER? 'u' :
+ cache_mode == CACHE_MODE_SSH? 's' : 'n',
+ keyinfo);
+ else
+- snprintf (line, DIM(line)-1, "SETKEYINFO --clear");
++ snprintf (line, DIM(line), "SETKEYINFO --clear");
+
+ rc = assuan_transact (entry_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD)
+ return unlock_pinentry (rc);
+
+- snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETDESC %s", desc_text);
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return unlock_pinentry (rc);
+
+- snprintf (line, DIM(line)-1, "SETPROMPT %s",
++ snprintf (line, DIM(line), "SETPROMPT %s",
+ prompt_text? prompt_text : is_pin? L_("PIN:") : L_("Passphrase:"));
+- line[DIM(line)-1] = 0;
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return unlock_pinentry (rc);
+@@ -924,8 +920,7 @@ agent_askpin (ctrl_t ctrl,
+
+ if (initial_errtext)
+ {
+- snprintf (line, DIM(line)-1, "SETERROR %s", initial_errtext);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETERROR %s", initial_errtext);
+ rc = assuan_transact (entry_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+@@ -934,9 +929,8 @@ agent_askpin (ctrl_t ctrl,
+
+ if (pininfo->with_repeat)
+ {
+- snprintf (line, DIM(line)-1, "SETREPEATERROR %s",
++ snprintf (line, DIM(line), "SETREPEATERROR %s",
+ L_("does not match - try again"));
+- line[DIM(line)-1] = 0;
+ rc = assuan_transact (entry_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+@@ -956,9 +950,8 @@ agent_askpin (ctrl_t ctrl,
+ /* TRANSLATORS: The string is appended to an error message in
+ the pinentry. The %s is the actual error message, the
+ two %d give the current and maximum number of tries. */
+- snprintf (line, DIM(line)-1, L_("SETERROR %s (try %d of %d)"),
++ snprintf (line, DIM(line), L_("SETERROR %s (try %d of %d)"),
+ errtext, pininfo->failed_tries+1, pininfo->max_tries);
+- line[DIM(line)-1] = 0;
+ rc = assuan_transact (entry_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+@@ -968,8 +961,7 @@ agent_askpin (ctrl_t ctrl,
+
+ if (pininfo->with_repeat)
+ {
+- snprintf (line, DIM(line)-1, "SETREPEAT %s", L_("Repeat:"));
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETREPEAT %s", L_("Repeat:"));
+ rc = assuan_transact (entry_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+@@ -1100,12 +1092,12 @@ agent_get_passphrase (ctrl_t ctrl,
+ if (keyinfo && (cache_mode == CACHE_MODE_NORMAL
+ || cache_mode == CACHE_MODE_USER
+ || cache_mode == CACHE_MODE_SSH))
+- snprintf (line, DIM(line)-1, "SETKEYINFO %c/%s",
++ snprintf (line, DIM(line), "SETKEYINFO %c/%s",
+ cache_mode == CACHE_MODE_USER? 'u' :
+ cache_mode == CACHE_MODE_SSH? 's' : 'n',
+ keyinfo);
+ else
+- snprintf (line, DIM(line)-1, "SETKEYINFO --clear");
++ snprintf (line, DIM(line), "SETKEYINFO --clear");
+
+ rc = assuan_transact (entry_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+@@ -1114,16 +1106,14 @@ agent_get_passphrase (ctrl_t ctrl,
+
+
+ if (desc)
+- snprintf (line, DIM(line)-1, "SETDESC %s", desc);
++ snprintf (line, DIM(line), "SETDESC %s", desc);
+ else
+- snprintf (line, DIM(line)-1, "RESET");
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "RESET");
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return unlock_pinentry (rc);
+
+- snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETPROMPT %s", prompt);
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return unlock_pinentry (rc);
+@@ -1137,8 +1127,7 @@ agent_get_passphrase (ctrl_t ctrl,
+
+ if (errtext)
+ {
+- snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETERROR %s", errtext);
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return unlock_pinentry (rc);
+@@ -1205,10 +1194,9 @@ agent_get_confirmation (ctrl_t ctrl,
+ return rc;
+
+ if (desc)
+- snprintf (line, DIM(line)-1, "SETDESC %s", desc);
++ snprintf (line, DIM(line), "SETDESC %s", desc);
+ else
+- snprintf (line, DIM(line)-1, "RESET");
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "RESET");
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ /* Most pinentries out in the wild return the old Assuan error code
+ for canceled which gets translated to an assuan Cancel error and
+@@ -1221,8 +1209,7 @@ agent_get_confirmation (ctrl_t ctrl,
+
+ if (ok)
+ {
+- snprintf (line, DIM(line)-1, "SETOK %s", ok);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETOK %s", ok);
+ rc = assuan_transact (entry_ctx,
+ line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+@@ -1235,8 +1222,7 @@ agent_get_confirmation (ctrl_t ctrl,
+ the standard cancel. */
+ if (with_cancel)
+ {
+- snprintf (line, DIM(line)-1, "SETNOTOK %s", notok);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETNOTOK %s", notok);
+ rc = assuan_transact (entry_ctx,
+ line, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+@@ -1245,8 +1231,7 @@ agent_get_confirmation (ctrl_t ctrl,
+
+ if (gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD)
+ {
+- snprintf (line, DIM(line)-1, "SETCANCEL %s", notok);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETCANCEL %s", notok);
+ rc = assuan_transact (entry_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+@@ -1282,10 +1267,9 @@ agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn)
+ return rc;
+
+ if (desc)
+- snprintf (line, DIM(line)-1, "SETDESC %s", desc);
++ snprintf (line, DIM(line), "SETDESC %s", desc);
+ else
+- snprintf (line, DIM(line)-1, "RESET");
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "RESET");
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ /* Most pinentries out in the wild return the old Assuan error code
+ for canceled which gets translated to an assuan Cancel error and
+@@ -1298,8 +1282,7 @@ agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn)
+
+ if (ok_btn)
+ {
+- snprintf (line, DIM(line)-1, "SETOK %s", ok_btn);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETOK %s", ok_btn);
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ if (rc)
+@@ -1354,18 +1337,16 @@ agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn)
+ return rc;
+
+ if (desc)
+- snprintf (line, DIM(line)-1, "SETDESC %s", desc);
++ snprintf (line, DIM(line), "SETDESC %s", desc);
+ else
+- snprintf (line, DIM(line)-1, "RESET");
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "RESET");
+ rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return unlock_pinentry (rc);
+
+ if (ok_btn)
+ {
+- snprintf (line, DIM(line)-1, "SETOK %s", ok_btn);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETOK %s", ok_btn);
+ rc = assuan_transact (entry_ctx, line, NULL,NULL,NULL,NULL,NULL,NULL);
+ if (rc)
+ return unlock_pinentry (rc);
+@@ -1465,7 +1446,7 @@ agent_clear_passphrase (ctrl_t ctrl,
+ if (rc)
+ return rc;
+
+- snprintf (line, DIM(line)-1, "CLEARPASSPHRASE %c/%s",
++ snprintf (line, DIM(line), "CLEARPASSPHRASE %c/%s",
+ cache_mode == CACHE_MODE_USER? 'u' :
+ cache_mode == CACHE_MODE_SSH? 's' : 'n',
+ keyinfo);
+diff --git a/agent/call-scd.c b/agent/call-scd.c
+index 934ab4c..0f7d570 100644
+--- a/agent/call-scd.c
++++ b/agent/call-scd.c
+@@ -946,8 +946,7 @@ agent_card_pkdecrypt (ctrl_t ctrl,
+ inqparm.getpin_cb_arg = getpin_cb_arg;
+ inqparm.passthru = 0;
+ inqparm.any_inq_seen = 0;
+- snprintf (line, DIM(line)-1, "PKDECRYPT %s", keyid);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "PKDECRYPT %s", keyid);
+ rc = assuan_transact (ctrl->scd_local->ctx, line,
+ put_membuf_cb, &data,
+ inq_needpin, &inqparm,
+@@ -986,8 +985,7 @@ agent_card_readcert (ctrl_t ctrl,
+ return rc;
+
+ init_membuf (&data, 1024);
+- snprintf (line, DIM(line)-1, "READCERT %s", id);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "READCERT %s", id);
+ rc = assuan_transact (ctrl->scd_local->ctx, line,
+ put_membuf_cb, &data,
+ NULL, NULL,
+@@ -1022,8 +1020,7 @@ agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf)
+ return rc;
+
+ init_membuf (&data, 1024);
+- snprintf (line, DIM(line)-1, "READKEY %s", id);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "READKEY %s", id);
+ rc = assuan_transact (ctrl->scd_local->ctx, line,
+ put_membuf_cb, &data,
+ NULL, NULL,
+@@ -1088,8 +1085,7 @@ agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
+ if (rc)
+ return rc;
+
+- snprintf (line, DIM(line)-1, "WRITEKEY %s%s", force ? "--force " : "", id);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "WRITEKEY %s%s", force ? "--force " : "", id);
+ parms.ctx = ctrl->scd_local->ctx;
+ parms.getpin_cb = getpin_cb;
+ parms.getpin_cb_arg = getpin_cb_arg;
+diff --git a/agent/command.c b/agent/command.c
+index 1cab1d4..b17c62d 100644
+--- a/agent/command.c
++++ b/agent/command.c
+@@ -362,7 +362,7 @@ agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid)
+ if (!ctrl || !ctrl->server_local
+ || !ctrl->server_local->allow_pinentry_notify)
+ return 0;
+- snprintf (line, DIM(line)-1, "PINENTRY_LAUNCHED %lu", pid);
++ snprintf (line, DIM(line), "PINENTRY_LAUNCHED %lu", pid);
+ return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
+ }
+
+diff --git a/build-aux/speedo/w32/g4wihelp.c b/build-aux/speedo/w32/g4wihelp.c
+index d2c93e7..fe903aa 100644
+--- a/build-aux/speedo/w32/g4wihelp.c
++++ b/build-aux/speedo/w32/g4wihelp.c
+@@ -70,12 +70,12 @@ dummy (HWND hwndParent, int string_size, char *variables,
+ // do your stuff here
+ {
+ char buf[1024];
+- snprintf (buf, sizeof buf - 1, "$R0=%s\r\n$R1=%s\r\n",
++ snprintf (buf, sizeof buf, "$R0=%s\r\n$R1=%s\r\n",
+ getuservariable(INST_R0),
+ getuservariable(INST_R1));
+ MessageBox (g_hwndParent,buf,0,MB_OK);
+
+- snprintf (buf, sizeof buf - 1,
++ snprintf (buf, sizeof buf,
+ "autoclose =%d\r\n"
+ "all_user_var =%d\r\n"
+ "exec_error =%d\r\n"
+@@ -278,7 +278,7 @@ void
+ service_error (const char *str)
+ {
+ char buf[1024];
+- snprintf (buf, sizeof (buf) - 1, "error: %s: ec=%d\r\n", str,
++ snprintf (buf, sizeof (buf), "error: %s: ec=%d\r\n", str,
+ GetLastError ());
+ MessageBox(g_hwndParent, buf, 0, MB_OK);
+
+@@ -575,7 +575,7 @@ service_stop (HWND hwndParent, int string_size, char *variables,
+ if (GetTickCount () - start_time > timeout)
+ {
+ char buf[1024];
+- snprintf (buf, sizeof (buf) - 1,
++ snprintf (buf, sizeof (buf),
+ "time out waiting for service %s to stop\r\n",
+ service_name);
+ MessageBox (g_hwndParent, buf, 0, MB_OK);
+diff --git a/common/get-passphrase.c b/common/get-passphrase.c
+index 8f3137b..46a7835 100644
+--- a/common/get-passphrase.c
++++ b/common/get-passphrase.c
+@@ -181,7 +181,7 @@ gnupg_get_passphrase (const char *cache_id,
+ if (!(arg4 = percent_plus_escape (desc_msg)))
+ goto no_mem;
+
+- snprintf (line, DIM(line)-1,
++ snprintf (line, DIM(line),
+ "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s",
+ check_quality? "--check ":"",
+ repeat,
+@@ -189,7 +189,6 @@ gnupg_get_passphrase (const char *cache_id,
+ arg2? arg2:"X",
+ arg3? arg3:"X",
+ arg4? arg4:"X");
+- line[DIM(line)-1] = 0;
+ xfree (arg2);
+ xfree (arg3);
+ xfree (arg4);
+@@ -250,8 +249,7 @@ gnupg_clear_passphrase (const char *cache_id)
+ if (err)
+ return err;
+
+- snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "CLEAR_PASSPHRASE %s", cache_id);
+ return assuan_transact (agent_ctx, line, NULL, NULL,
+ default_inq_cb, NULL, NULL, NULL);
+ }
+diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c
+index 2bbc0ed..ba9f96d 100644
+--- a/dirmngr/dirmngr.c
++++ b/dirmngr/dirmngr.c
+@@ -2050,9 +2050,8 @@ handle_connections (assuan_fd_t listen_fd)
+
+ memset (&argval, 0, sizeof argval);
+ argval.afd = fd;
+- snprintf (threadname, sizeof threadname-1,
++ snprintf (threadname, sizeof threadname,
+ "conn fd=%d", FD2INT(fd));
+- threadname[sizeof threadname -1] = 0;
+
+ ret = npth_create (&thread, &tattr,
+ start_connection_thread, argval.aptr);
+diff --git a/g10/call-agent.c b/g10/call-agent.c
+index 632cabe..c1ad8dd 100644
+--- a/g10/call-agent.c
++++ b/g10/call-agent.c
+@@ -726,7 +726,7 @@ agent_scd_apdu (const char *hexapdu, unsigned int *r_sw)
+
+ init_membuf (&mb, 256);
+
+- snprintf (line, DIM(line)-1, "SCD APDU %s", hexapdu);
++ snprintf (line, DIM(line), "SCD APDU %s", hexapdu);
+ err = assuan_transact (agent_ctx, line,
+ put_membuf_cb, &mb, NULL, NULL, NULL, NULL);
+ if (!err)
+@@ -758,9 +758,8 @@ agent_keytocard (const char *hexgrip, int keyno, int force,
+
+ memset (&parm, 0, sizeof parm);
+
+- snprintf (line, DIM(line)-1, "KEYTOCARD %s%s %s OPENPGP.%d %s",
++ snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s",
+ force?"--force ": "", hexgrip, serialno, keyno, timestamp);
+- line[DIM(line)-1] = 0;
+
+ rc = start_agent (NULL, 1);
+ if (rc)
+@@ -902,8 +901,7 @@ agent_scd_writecert (const char *certidstr,
+
+ memset (&parms, 0, sizeof parms);
+
+- snprintf (line, DIM(line)-1, "SCD WRITECERT %s", certidstr);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SCD WRITECERT %s", certidstr);
+ dfltparm.ctx = agent_ctx;
+ parms.dflt = &dfltparm;
+ parms.certdata = certdata;
+@@ -956,8 +954,7 @@ agent_scd_writekey (int keyno, const char *serialno,
+
+ memset (&parms, 0, sizeof parms);
+
+- snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SCD WRITEKEY --force OPENPGP.%d", keyno);
+ dfltparm.ctx = agent_ctx;
+ parms.dflt = &dfltparm;
+ parms.keydata = keydata;
+@@ -1019,11 +1016,10 @@ agent_scd_genkey (int keyno, int force, u32 *createtime)
+ else
+ *tbuf = 0;
+
+- snprintf (line, DIM(line)-1, "SCD GENKEY %s%s %s %d",
++ snprintf (line, DIM(line), "SCD GENKEY %s%s %s %d",
+ *tbuf? "--timestamp=":"", tbuf,
+ force? "--force":"",
+ keyno);
+- line[DIM(line)-1] = 0;
+
+ dfltparm.ctx = agent_ctx;
+ rc = assuan_transact (agent_ctx, line,
+@@ -1151,8 +1147,7 @@ agent_scd_readcert (const char *certidstr,
+
+ init_membuf (&data, 2048);
+
+- snprintf (line, DIM(line)-1, "SCD READCERT %s", certidstr);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SCD READCERT %s", certidstr);
+ rc = assuan_transact (agent_ctx, line,
+ put_membuf_cb, &data,
+ default_inq_cb, &dfltparm,
+@@ -1202,8 +1197,7 @@ agent_scd_change_pin (int chvno, const char *serialno)
+ return rc;
+ dfltparm.ctx = agent_ctx;
+
+- snprintf (line, DIM(line)-1, "SCD PASSWD %s %d", reset, chvno);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SCD PASSWD %s %d", reset, chvno);
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL,
+ default_inq_cb, &dfltparm,
+@@ -1230,8 +1224,7 @@ agent_scd_checkpin (const char *serialno)
+ return rc;
+ dfltparm.ctx = agent_ctx;
+
+- snprintf (line, DIM(line)-1, "SCD CHECKPIN %s", serialno);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SCD CHECKPIN %s", serialno);
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL,
+ default_inq_cb, &dfltparm,
+@@ -1301,7 +1294,7 @@ agent_get_passphrase (const char *cache_id,
+ if (!(arg4 = percent_plus_escape (desc_msg)))
+ goto no_mem;
+
+- snprintf (line, DIM(line)-1,
++ snprintf (line, DIM(line),
+ "GET_PASSPHRASE --data --repeat=%d%s -- %s %s %s %s",
+ repeat,
+ check? " --check --qualitybar":"",
+@@ -1309,7 +1302,6 @@ agent_get_passphrase (const char *cache_id,
+ arg2? arg2:"X",
+ arg3? arg3:"X",
+ arg4? arg4:"X");
+- line[DIM(line)-1] = 0;
+ xfree (arg1);
+ xfree (arg2);
+ xfree (arg3);
+@@ -1358,8 +1350,7 @@ agent_clear_passphrase (const char *cache_id)
+ return rc;
+ dfltparm.ctx = agent_ctx;
+
+- snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "CLEAR_PASSPHRASE %s", cache_id);
+ return assuan_transact (agent_ctx, line,
+ NULL, NULL,
+ default_inq_cb, &dfltparm,
+@@ -1387,8 +1378,7 @@ gpg_agent_get_confirmation (const char *desc)
+ tmp = percent_plus_escape (desc);
+ if (!tmp)
+ return gpg_error_from_syserror ();
+- snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", tmp);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "GET_CONFIRMATION %s", tmp);
+ xfree (tmp);
+
+ rc = assuan_transact (agent_ctx, line,
+@@ -1574,8 +1564,7 @@ agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
+ if (!hexkeygrip || strlen (hexkeygrip) != 40)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+- snprintf (line, DIM(line)-1, "KEYINFO %s", hexkeygrip);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip);
+
+ err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
+ keyinfo_status_cb, &keyinfo);
+@@ -1761,7 +1750,7 @@ agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+ if (err)
+ return err;
+
+- snprintf (line, DIM(line)-1, "READKEY %s%s", fromcard? "--card ":"",
++ snprintf (line, DIM(line), "READKEY %s%s", fromcard? "--card ":"",
+ hexkeygrip);
+
+ init_membuf (&data, 1024);
+@@ -1826,16 +1815,14 @@ agent_pksign (ctrl_t ctrl, const char *cache_nonce,
+ if (err)
+ return err;
+
+- snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SIGKEY %s", keygrip);
+ err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+@@ -1966,8 +1953,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+@@ -2059,7 +2045,7 @@ agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen)
+ return err;
+ dfltparm.ctx = agent_ctx;
+
+- snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s",
++ snprintf (line, DIM(line), "KEYWRAP_KEY %s",
+ forexport? "--export":"--import");
+
+ init_membuf_secure (&data, 64);
+@@ -2121,8 +2107,7 @@ agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+@@ -2182,14 +2167,14 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+ }
+
+- snprintf (line, DIM(line)-1, "EXPORT_KEY %s%s%s %s",
++ snprintf (line, DIM(line), "EXPORT_KEY %s%s%s %s",
+ openpgp_protected ? "--openpgp ":"",
+ cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"",
+ cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"",
+@@ -2241,14 +2226,14 @@ agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+ }
+
+- snprintf (line, DIM(line)-1, "DELETE_KEY%s %s",
++ snprintf (line, DIM(line), "DELETE_KEY%s %s",
+ force? " --force":"", hexkeygrip);
+ err = assuan_transact (agent_ctx, line, NULL, NULL,
+ default_inq_cb, &dfltparm,
+@@ -2287,7 +2272,7 @@ agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int verify,
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+@@ -2295,12 +2280,12 @@ agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int verify,
+ }
+
+ if (verify)
+- snprintf (line, DIM(line)-1, "PASSWD %s%s --verify %s",
++ snprintf (line, DIM(line), "PASSWD %s%s --verify %s",
+ cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"",
+ cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"",
+ hexkeygrip);
+ else
+- snprintf (line, DIM(line)-1, "PASSWD %s%s %s%s %s",
++ snprintf (line, DIM(line), "PASSWD %s%s %s%s %s",
+ cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"",
+ cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"",
+ passwd_nonce_addr && *passwd_nonce_addr? "--passwd-nonce=":"",
+diff --git a/g10/cpr.c b/g10/cpr.c
+index 9d8fec9..7760847 100644
+--- a/g10/cpr.c
++++ b/g10/cpr.c
+@@ -53,9 +53,9 @@ progress_cb (void *ctx, const char *what, int printchar,
+ (void)ctx;
+
+ if ( printchar == '\n' && !strcmp (what, "primegen") )
+- snprintf (buf, sizeof buf -1, "%.20s X 100 100", what );
++ snprintf (buf, sizeof buf, "%.20s X 100 100", what );
+ else
+- snprintf (buf, sizeof buf -1, "%.20s %c %d %d",
++ snprintf (buf, sizeof buf, "%.20s %c %d %d",
+ what, printchar=='\n'?'X':printchar, current, total );
+ write_status_text (STATUS_PROGRESS, buf);
+ }
+@@ -329,7 +329,7 @@ write_status_begin_signing (gcry_md_hd_t md)
+ ga = map_md_openpgp_to_gcry (i);
+ if (ga && gcry_md_is_enabled (md, ga) && buflen+10 < DIM(buf))
+ {
+- snprintf (buf+buflen, DIM(buf) - buflen - 1,
++ snprintf (buf+buflen, DIM(buf) - buflen,
+ "%sH%d", buflen? " ":"",i);
+ buflen += strlen (buf+buflen);
+ }
+diff --git a/g10/keygen.c b/g10/keygen.c
+index 2115b5a..5ff89f6 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -4877,7 +4877,6 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+ gcry_sexp_t s_key;
+
+ snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno);
+- keyid[DIM(keyid)-1] = 0;
+
+ if (algo != PUBKEY_ALGO_RSA)
+ return gpg_error (GPG_ERR_PUBKEY_ALGO);
+diff --git a/g10/openfile.c b/g10/openfile.c
+index 006ff35..ad25604 100644
+--- a/g10/openfile.c
++++ b/g10/openfile.c
+@@ -148,9 +148,9 @@ ask_outfile_name( const char *name, size_t namelen )
+ n = strlen(s) + (defname?strlen (defname):0) + 10;
+ prompt = xmalloc (n);
+ if (defname)
+- snprintf (prompt, n-1, "%s [%s]: ", s, defname );
++ snprintf (prompt, n, "%s [%s]: ", s, defname );
+ else
+- snprintf (prompt, n-1, "%s: ", s );
++ snprintf (prompt, n, "%s: ", s );
+ tty_enable_completion(NULL);
+ fname = cpr_get ("openfile.askoutname", prompt );
+ cpr_kill_prompt ();
+diff --git a/g10/passphrase.c b/g10/passphrase.c
+index be71b68..d75d980 100644
+--- a/g10/passphrase.c
++++ b/g10/passphrase.c
+@@ -347,7 +347,7 @@ passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
+ {
+ char buf[50];
+
+- snprintf (buf, sizeof buf -1, "%d %d %d",
++ snprintf (buf, sizeof buf, "%d %d %d",
+ cipher_algo, s2k->mode, s2k->hash_algo );
+ write_status_text ( STATUS_NEED_PASSPHRASE_SYM, buf );
+ }
+@@ -447,7 +447,7 @@ emit_status_need_passphrase (u32 *keyid, u32 *mainkeyid, int pubkey_algo)
+ write_status_text (STATUS_USERID_HINT, us);
+ xfree (us);
+
+- snprintf (buf, sizeof buf -1, "%08lX%08lX %08lX%08lX %d 0",
++ snprintf (buf, sizeof buf, "%08lX%08lX %08lX%08lX %d 0",
+ (ulong)keyid[0],
+ (ulong)keyid[1],
+ (ulong)(mainkeyid? mainkeyid[0]:keyid[0]),
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index 843fdf0..0931095 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -1872,7 +1872,7 @@ verify_a_chv (app_t app,
+ prompt_buffer = xtrymalloc (promptsize);
+ if (!prompt_buffer)
+ return gpg_error_from_syserror ();
+- snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount);
++ snprintf (prompt_buffer, promptsize, PROMPTSTRING, sigcount);
+ prompt = prompt_buffer;
+ #undef PROMPTSTRING
+ }
+diff --git a/scd/scdaemon.c b/scd/scdaemon.c
+index 33a822e..0d26410 100644
+--- a/scd/scdaemon.c
++++ b/scd/scdaemon.c
+@@ -1292,8 +1292,7 @@ handle_connections (int listen_fd)
+ char threadname[50];
+ npth_t thread;
+
+- snprintf (threadname, sizeof threadname-1, "conn fd=%d", fd);
+- threadname[sizeof threadname -1] = 0;
++ snprintf (threadname, sizeof threadname, "conn fd=%d", fd);
+ ctrl->thread_startup.fd = INT2FD (fd);
+ ret = npth_create (&thread, &tattr, start_connection_thread, ctrl);
+ if (ret)
+diff --git a/sm/call-agent.c b/sm/call-agent.c
+index 3262650..c0a2081 100644
+--- a/sm/call-agent.c
++++ b/sm/call-agent.c
+@@ -243,16 +243,14 @@ gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
+ if (rc)
+ return rc;
+
+- snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SIGKEY %s", keygrip);
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+@@ -335,8 +333,7 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
+
+ init_membuf (&data, 1024);
+
+- snprintf (line, DIM(line)-1, "SCD PKSIGN %s %s", hashopt, keyid);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SCD PKSIGN %s %s", hashopt, keyid);
+ rc = assuan_transact (agent_ctx, line,
+ put_membuf_cb, &data, default_inq_cb, &inq_parm,
+ NULL, NULL);
+@@ -429,16 +426,14 @@ gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
+ return rc;
+
+ assert ( DIM(line) >= 50 );
+- snprintf (line, DIM(line)-1, "SETKEY %s", keygrip);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETKEY %s", keygrip);
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+@@ -594,9 +589,8 @@ gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
+ if (rc)
+ return rc;
+
+- snprintf (line, DIM(line)-1, "%sREADKEY %s",
++ snprintf (line, DIM(line), "%sREADKEY %s",
+ fromcard? "SCD ":"", hexkeygrip);
+- line[DIM(line)-1] = 0;
+
+ init_membuf (&data, 1024);
+ rc = assuan_transact (agent_ctx, line,
+@@ -810,8 +804,7 @@ gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr,
+
+ if (hexfpr)
+ {
+- snprintf (line, DIM(line)-1, "ISTRUSTED %s", hexfpr);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "ISTRUSTED %s", hexfpr);
+ }
+ else
+ {
+@@ -824,8 +817,7 @@ gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr,
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+- snprintf (line, DIM(line)-1, "ISTRUSTED %s", fpr);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "ISTRUSTED %s", fpr);
+ xfree (fpr);
+ }
+
+@@ -868,8 +860,7 @@ gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert)
+ xfree (dn);
+ if (!dnfmt)
+ return gpg_error_from_syserror ();
+- snprintf (line, DIM(line)-1, "MARKTRUSTED %s S %s", fpr, dnfmt);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "MARKTRUSTED %s S %s", fpr, dnfmt);
+ ksba_free (dnfmt);
+ xfree (fpr);
+
+@@ -895,8 +886,7 @@ gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip)
+ if (!hexkeygrip || strlen (hexkeygrip) != 40)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+- snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "HAVEKEY %s", hexkeygrip);
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ return rc;
+@@ -1045,16 +1035,14 @@ gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ rc = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ return rc;
+ }
+
+- snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "PASSWD %s", hexkeygrip);
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL,
+ default_inq_cb, &inq_parm, NULL, NULL);
+@@ -1078,8 +1066,7 @@ gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc)
+ inq_parm.ctrl = ctrl;
+ inq_parm.ctx = agent_ctx;
+
+- snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", desc);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "GET_CONFIRMATION %s", desc);
+
+ rc = assuan_transact (agent_ctx, line, NULL, NULL,
+ default_inq_cb, &inq_parm, NULL, NULL);
+@@ -1150,8 +1137,7 @@ gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
+ if (!hexkeygrip || strlen (hexkeygrip) != 40)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+- snprintf (line, DIM(line)-1, "KEYINFO %s", hexkeygrip);
+- line[DIM(line)-1] = 0;
++ snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip);
+
+ err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
+ keyinfo_status_cb, &serialno);
+@@ -1196,7 +1182,7 @@ gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat,
+ if (desc_msg && *desc_msg && !(arg4 = percent_plus_escape (desc_msg)))
+ return gpg_error_from_syserror ();
+
+- snprintf (line, DIM(line)-1, "GET_PASSPHRASE --data%s -- X X X %s",
++ snprintf (line, DIM(line), "GET_PASSPHRASE --data%s -- X X X %s",
+ repeat? " --repeat=1 --check --qualitybar":"",
+ arg4);
+ xfree (arg4);
+@@ -1241,7 +1227,7 @@ gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport,
+ inq_parm.ctrl = ctrl;
+ inq_parm.ctx = agent_ctx;
+
+- snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s",
++ snprintf (line, DIM(line), "KEYWRAP_KEY %s",
+ forexport? "--export":"--import");
+
+ init_membuf_secure (&data, 64);
+@@ -1335,14 +1321,14 @@ gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc,
+
+ if (desc)
+ {
+- snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
++ snprintf (line, DIM(line), "SETKEYDESC %s", desc);
+ err = assuan_transact (agent_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+ }
+
+- snprintf (line, DIM(line)-1, "EXPORT_KEY %s", keygrip);
++ snprintf (line, DIM(line), "EXPORT_KEY %s", keygrip);
+
+ init_membuf_secure (&data, 1024);
+ err = assuan_transact (agent_ctx, line,
+diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c
+index 6987121..4afc697 100644
+--- a/sm/call-dirmngr.c
++++ b/sm/call-dirmngr.c
+@@ -215,9 +215,8 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err)
+ char *pass = server->pass ? server->pass : "";
+ char *base = server->base ? server->base : "";
+
+- snprintf (line, DIM (line) - 1, "LDAPSERVER %s:%i:%s:%s:%s",
++ snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s",
+ server->host, server->port, user, pass, base);
+- line[DIM (line) - 1] = 0;
+
+ assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ /* The code below is not required becuase we don't return an error. */
+@@ -548,10 +547,9 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ did_options = 1;
+ }
+- snprintf (line, DIM(line)-1, "ISVALID%s %s",
++ snprintf (line, DIM(line), "ISVALID%s %s",
+ use_ocsp == 2? " --only-ocsp --force-default-responder":"",
+ certid);
+- line[DIM(line)-1] = 0;
+ xfree (certid);
+
+ rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
+@@ -803,9 +801,8 @@ gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only,
+
+ return out_of_core ();
+ }
+- snprintf (line, DIM(line)-1, "LOOKUP%s %s",
++ snprintf (line, DIM(line), "LOOKUP%s %s",
+ cache_only? " --cache-only":"", pattern);
+- line[DIM(line)-1] = 0;
+ xfree (pattern);
+
+ parm.ctrl = ctrl;
+@@ -861,7 +858,7 @@ get_cached_cert (assuan_context_t ctx,
+ *r_cert = NULL;
+
+ bin2hex (fpr, 20, hexfpr);
+- snprintf (line, DIM(line)-1, "LOOKUP --single --cache-only 0x%s", hexfpr);
++ snprintf (line, DIM(line), "LOOKUP --single --cache-only 0x%s", hexfpr);
+
+ init_membuf (&mb, 4096);
+ err = assuan_transact (ctx, line, get_cached_cert_data_cb, &mb,
+diff --git a/sm/certreqgen.c b/sm/certreqgen.c
+index 2c6550c..4d50270 100644
+--- a/sm/certreqgen.c
++++ b/sm/certreqgen.c
+@@ -719,7 +719,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
+ else if (!outctrl->dryrun) /* Generate new key. */
+ {
+ sprintf (numbuf, "%u", nbits);
+- snprintf ((char*)keyparms, DIM (keyparms)-1,
++ snprintf ((char*)keyparms, DIM (keyparms),
+ "(6:genkey(3:rsa(5:nbits%d:%s)))",
+ (int)strlen (numbuf), numbuf);
+ rc = gpgsm_agent_genkey (ctrl, keyparms, &public);
diff --git a/debian/patches/0084-g10-Support-ECC-for-gen_card_key.patch b/debian/patches/0084-g10-Support-ECC-for-gen_card_key.patch
new file mode 100644
index 0000000..c245c13
--- /dev/null
+++ b/debian/patches/0084-g10-Support-ECC-for-gen_card_key.patch
@@ -0,0 +1,126 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Fri, 21 Oct 2016 13:59:09 +0900
+Subject: g10: Support ECC for gen_card_key.
+
+* g10/keygen.c (gen_card_key): Remove the first argument of ALGO.
+(do_generate_keypair, generate_card_subkeypair): Follow the change.
+
+--
+ALGO is determined by the key attribute of the card.
+
+Co-authored-by: Arnaud Fontaine <arnaud.fontaine at ssi.gouv.fr>
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ g10/keygen.c | 32 +++++++++++++++++---------------
+ 1 file changed, 17 insertions(+), 15 deletions(-)
+
+diff --git a/g10/keygen.c b/g10/keygen.c
+index 5ff89f6..64e0d43 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -154,8 +154,7 @@ static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
+ static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+ struct output_control_s *outctrl, int card );
+ static int write_keyblock (iobuf_t out, kbnode_t node);
+-static gpg_error_t gen_card_key (int algo, int keyno, int is_primary,
+- kbnode_t pub_root,
++static gpg_error_t gen_card_key (int keyno, int is_primary, kbnode_t pub_root,
+ u32 *timestamp, u32 expireval);
+
+
+@@ -4238,8 +4237,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+ get_parameter_passphrase (para),
+ &cache_nonce, NULL);
+ else
+- err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root,
+- ×tamp,
++ err = gen_card_key (1, 1, pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
+
+ /* Get the pointer to the generated public key packet. */
+@@ -4277,8 +4275,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+
+ if (!err && card && get_parameter (para, pAUTHKEYTYPE))
+ {
+- err = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root,
+- ×tamp,
++ err = gen_card_key (3, 0, pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
+ if (!err)
+ err = write_keybinding (pub_root, pri_psk, NULL,
+@@ -4317,7 +4314,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+ }
+ else
+ {
+- err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, ×tamp,
++ err = gen_card_key (2, 0, pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
+ }
+
+@@ -4749,7 +4746,6 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
+ gpg_error_t err = 0;
+ kbnode_t node;
+ PKT_public_key *pri_pk = NULL;
+- int algo;
+ unsigned int use;
+ u32 expire;
+ u32 cur_time;
+@@ -4800,7 +4796,6 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
+ goto leave;
+ }
+
+- algo = PUBKEY_ALGO_RSA;
+ expire = ask_expire_interval (0, NULL);
+ if (keyno == 1)
+ use = PUBKEY_USAGE_SIG;
+@@ -4817,7 +4812,7 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
+
+ /* Note, that depending on the backend, the card key generation may
+ update CUR_TIME. */
+- err = gen_card_key (algo, keyno, 0, pub_keyblock, &cur_time, expire);
++ err = gen_card_key (keyno, 0, pub_keyblock, &cur_time, expire);
+ /* Get the pointer to the generated public subkey packet. */
+ if (!err)
+ {
+@@ -4865,21 +4860,29 @@ write_keyblock( IOBUF out, KBNODE node )
+
+ /* Note that timestamp is an in/out arg. */
+ static gpg_error_t
+-gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
++gen_card_key (int keyno, int is_primary, kbnode_t pub_root,
+ u32 *timestamp, u32 expireval)
+ {
+ #ifdef ENABLE_CARD_SUPPORT
+ gpg_error_t err;
++ struct agent_card_info_s info;
++ int algo;
+ PACKET *pkt;
+ PKT_public_key *pk;
+ char keyid[10];
+ unsigned char *public;
+ gcry_sexp_t s_key;
+
+- snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno);
++ err = agent_scd_getattr ("KEY-ATTR", &info);
++ if (err)
++ {
++ log_error (_("error getting current key info: %s\n"), gpg_strerror (err));
++ return err;
++ }
+
+- if (algo != PUBKEY_ALGO_RSA)
+- return gpg_error (GPG_ERR_PUBKEY_ALGO);
++ algo = info.key_attr[keyno-1].algo;
++
++ snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno);
+
+ pk = xtrycalloc (1, sizeof *pk );
+ if (!pk)
+@@ -4954,7 +4957,6 @@ gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root,
+
+ return 0;
+ #else
+- (void)algo;
+ (void)keyno;
+ (void)is_primary;
+ (void)pub_root;
diff --git a/debian/patches/0085-g10-Don-t-ask-keysize-for-for-non-RSA-card.patch b/debian/patches/0085-g10-Don-t-ask-keysize-for-for-non-RSA-card.patch
new file mode 100644
index 0000000..e31b0fa
--- /dev/null
+++ b/debian/patches/0085-g10-Don-t-ask-keysize-for-for-non-RSA-card.patch
@@ -0,0 +1,108 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Fri, 21 Oct 2016 14:15:05 +0900
+Subject: g10: Don't ask keysize for for non-RSA card.
+
+* g10/card-util.c (card_status): Bug fix for keyno.
+(ask_card_rsa_keysize, do_change_rsa_keysize): Rename.
+(generate_card_keys): Only ask keysize when RSA.
+(card_generate_subkey): Likewise.
+
+--
+
+Co-authored-by: Arnaud Fontaine <arnaud.fontaine at ssi.gouv.fr>
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ g10/card-util.c | 50 ++++++++++++++++++++++++++++----------------------
+ 1 file changed, 28 insertions(+), 22 deletions(-)
+
+diff --git a/g10/card-util.c b/g10/card-util.c
+index 2cb44f9..2f3f714 100644
+--- a/g10/card-util.c
++++ b/g10/card-util.c
+@@ -476,7 +476,7 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
+
+ es_fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
+ for (i=0; i < DIM (info.key_attr); i++)
+- if (info.key_attr[0].algo == PUBKEY_ALGO_RSA)
++ if (info.key_attr[i].algo == PUBKEY_ALGO_RSA)
+ es_fprintf (fp, "keyattr:%d:%d:%u:\n", i+1,
+ info.key_attr[i].algo, info.key_attr[i].nbits);
+ else if (info.key_attr[i].algo == PUBKEY_ALGO_ECDH
+@@ -1277,7 +1277,7 @@ show_keysize_warning (void)
+ select the prompt. Returns 0 to use the default size (i.e. NBITS)
+ or the selected size. */
+ static unsigned int
+-ask_card_keysize (int keyno, unsigned int nbits)
++ask_card_rsa_keysize (int keyno, unsigned int nbits)
+ {
+ unsigned int min_nbits = 1024;
+ unsigned int max_nbits = 4096;
+@@ -1327,7 +1327,7 @@ ask_card_keysize (int keyno, unsigned int nbits)
+ /* Change the size of key KEYNO (0..2) to NBITS and show an error
+ message if that fails. */
+ static gpg_error_t
+-do_change_keysize (int keyno, unsigned int nbits)
++do_change_rsa_keysize (int keyno, unsigned int nbits)
+ {
+ gpg_error_t err;
+ char args[100];
+@@ -1406,15 +1406,18 @@ generate_card_keys (ctrl_t ctrl)
+
+ for (keyno = 0; keyno < DIM (info.key_attr); keyno++)
+ {
+- nbits = ask_card_keysize (keyno, info.key_attr[keyno].nbits);
+- if (nbits && do_change_keysize (keyno, nbits))
++ if (info.key_attr[keyno].algo == PUBKEY_ALGO_RSA)
+ {
+- /* Error: Better read the default key size again. */
+- agent_release_card_info (&info);
+- if (get_info_for_key_operation (&info))
+- goto leave;
+- /* Ask again for this key size. */
+- keyno--;
++ nbits = ask_card_rsa_keysize (keyno, info.key_attr[keyno].nbits);
++ if (nbits && do_change_rsa_keysize (keyno, nbits))
++ {
++ /* Error: Better read the default key size again. */
++ agent_release_card_info (&info);
++ if (get_info_for_key_operation (&info))
++ goto leave;
++ /* Ask again for this key size. */
++ keyno--;
++ }
+ }
+ }
+ /* Note that INFO has not be synced. However we will only use
+@@ -1483,18 +1486,21 @@ card_generate_subkey (KBNODE pub_keyblock)
+ key size. */
+ if (info.is_v2 && info.extcap.aac)
+ {
+- unsigned int nbits;
+-
+- ask_again:
+- nbits = ask_card_keysize (keyno-1, info.key_attr[keyno-1].nbits);
+- if (nbits && do_change_keysize (keyno-1, nbits))
++ if (info.key_attr[keyno-1].algo == PUBKEY_ALGO_RSA)
+ {
+- /* Error: Better read the default key size again. */
+- agent_release_card_info (&info);
+- err = get_info_for_key_operation (&info);
+- if (err)
+- goto leave;
+- goto ask_again;
++ unsigned int nbits;
++
++ ask_again:
++ nbits = ask_card_rsa_keysize (keyno-1, info.key_attr[keyno-1].nbits);
++ if (nbits && do_change_rsa_keysize (keyno-1, nbits))
++ {
++ /* Error: Better read the default key size again. */
++ agent_release_card_info (&info);
++ err = get_info_for_key_operation (&info);
++ if (err)
++ goto leave;
++ goto ask_again;
++ }
+ }
+ /* Note that INFO has not be synced. However we will only use
+ the serialnumber and thus it won't harm. */
diff --git a/debian/patches/0086-scd-Fix-segfault-changing-key-attr.patch b/debian/patches/0086-scd-Fix-segfault-changing-key-attr.patch
new file mode 100644
index 0000000..b9fb2b5
--- /dev/null
+++ b/debian/patches/0086-scd-Fix-segfault-changing-key-attr.patch
@@ -0,0 +1,33 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Fri, 21 Oct 2016 16:27:46 +0900
+Subject: scd: Fix segfault changing key attr.
+
+* asc/app-openpgp.c (change_keyattr_from_string): Release after
+allocated.
+--
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ scd/app-openpgp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index 0931095..f909c6f 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -2987,7 +2987,6 @@ change_keyattr_from_string (app_t app,
+ size_t oid_len;
+
+ oidstr = openpgp_curve_to_oid (string+n, NULL);
+- gcry_mpi_release (oid);
+ if (!oidstr)
+ {
+ err = gpg_error (GPG_ERR_INV_DATA);
+@@ -3005,6 +3004,7 @@ change_keyattr_from_string (app_t app,
+ string[0] = algo;
+ memcpy (string+1, oidbuf+1, oid_len-1);
+ err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
++ gcry_mpi_release (oid);
+ }
+ else
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
diff --git a/debian/patches/0087-g10-scd-Fix-ECC-keygen.patch b/debian/patches/0087-g10-scd-Fix-ECC-keygen.patch
new file mode 100644
index 0000000..0f2ba5d
--- /dev/null
+++ b/debian/patches/0087-g10-scd-Fix-ECC-keygen.patch
@@ -0,0 +1,236 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Fri, 21 Oct 2016 21:37:04 +0900
+Subject: g10,scd: Fix ECC keygen.
+
+* g10/keygen.c (generate_keypair): For card key generation, fill
+parameters by KEY-ATTR.
+
+* scd/app-openpgp.c (ecc_read_pubkey): OID should be freed at last,
+after its reference by OIDBUF is finished.
+(ecc_writekey): Likewise.
+--
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ g10/call-agent.c | 8 +++++---
+ g10/keygen.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-------
+ scd/app-openpgp.c | 23 +++++++++++----------
+ 3 files changed, 70 insertions(+), 21 deletions(-)
+
+diff --git a/g10/call-agent.c b/g10/call-agent.c
+index c1ad8dd..e7af001 100644
+--- a/g10/call-agent.c
++++ b/g10/call-agent.c
+@@ -994,9 +994,11 @@ scd_genkey_cb (void *opaque, const char *line)
+ return 0;
+ }
+
+-/* Send a GENKEY command to the SCdaemon. If CREATETIME is not 0, it
+- will be passed to SCDAEMON so that the key is created with this
+- timestamp. On success, creation time is stored back to CREATETIME. */
++/* Send a GENKEY command to the SCdaemon. If *CREATETIME is not 0,
++ the value will be passed to SCDAEMON with --timestamp option so that
++ the key is created with this. Otherwise, timestamp was generated by
++ SCDEAMON. On success, creation time is stored back to
++ CREATETIME. */
+ int
+ agent_scd_genkey (int keyno, int force, u32 *createtime)
+ {
+diff --git a/g10/keygen.c b/g10/keygen.c
+index 64e0d43..a59435d 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -3756,17 +3756,26 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
+ if (card_serialno)
+ {
+ #ifdef ENABLE_CARD_SUPPORT
++ gpg_error_t err;
++ struct agent_card_info_s info;
++
++ memset (&info, 0, sizeof (info));
++ err = agent_scd_getattr ("KEY-ATTR", &info);
++ if (err)
++ {
++ log_error (_("error getting current key info: %s\n"), gpg_strerror (err));
++ return;
++ }
++
+ r = xcalloc (1, sizeof *r + strlen (card_serialno) );
+ r->key = pSERIALNO;
+ strcpy( r->u.value, card_serialno);
+ r->next = para;
+ para = r;
+
+- algo = PUBKEY_ALGO_RSA;
+-
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pKEYTYPE;
+- sprintf( r->u.value, "%d", algo );
++ sprintf( r->u.value, "%d", info.key_attr[0].algo );
+ r->next = para;
+ para = r;
+ r = xcalloc (1, sizeof *r + 20 );
+@@ -3774,10 +3783,28 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
+ strcpy (r->u.value, "sign");
+ r->next = para;
+ para = r;
++ if (info.key_attr[0].algo == PUBKEY_ALGO_RSA)
++ {
++ r = xcalloc (1, sizeof *r + 20 );
++ r->key = pKEYLENGTH;
++ sprintf( r->u.value, "%u", info.key_attr[0].nbits);
++ r->next = para;
++ para = r;
++ }
++ else if (info.key_attr[0].algo == PUBKEY_ALGO_ECDSA
++ || info.key_attr[0].algo == PUBKEY_ALGO_EDDSA
++ || info.key_attr[0].algo == PUBKEY_ALGO_ECDH)
++ {
++ r = xcalloc (1, sizeof *r + strlen (info.key_attr[0].curve));
++ r->key = pKEYCURVE;
++ strcpy (r->u.value, info.key_attr[0].curve);
++ r->next = para;
++ para = r;
++ }
+
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pSUBKEYTYPE;
+- sprintf( r->u.value, "%d", algo );
++ sprintf( r->u.value, "%d", info.key_attr[1].algo );
+ r->next = para;
+ para = r;
+ r = xcalloc (1, sizeof *r + 20 );
+@@ -3785,10 +3812,28 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
+ strcpy (r->u.value, "encrypt");
+ r->next = para;
+ para = r;
++ if (info.key_attr[1].algo == PUBKEY_ALGO_RSA)
++ {
++ r = xcalloc (1, sizeof *r + 20 );
++ r->key = pSUBKEYLENGTH;
++ sprintf( r->u.value, "%u", info.key_attr[1].nbits);
++ r->next = para;
++ para = r;
++ }
++ else if (info.key_attr[1].algo == PUBKEY_ALGO_ECDSA
++ || info.key_attr[1].algo == PUBKEY_ALGO_EDDSA
++ || info.key_attr[1].algo == PUBKEY_ALGO_ECDH)
++ {
++ r = xcalloc (1, sizeof *r + strlen (info.key_attr[1].curve));
++ r->key = pSUBKEYCURVE;
++ strcpy (r->u.value, info.key_attr[1].curve);
++ r->next = para;
++ para = r;
++ }
+
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pAUTHKEYTYPE;
+- sprintf( r->u.value, "%d", algo );
++ sprintf( r->u.value, "%d", info.key_attr[2].algo );
+ r->next = para;
+ para = r;
+
+@@ -4873,6 +4918,7 @@ gen_card_key (int keyno, int is_primary, kbnode_t pub_root,
+ unsigned char *public;
+ gcry_sexp_t s_key;
+
++ memset (&info, 0, sizeof (info));
+ err = agent_scd_getattr ("KEY-ATTR", &info);
+ if (err)
+ {
+@@ -4931,8 +4977,8 @@ gen_card_key (int keyno, int is_primary, kbnode_t pub_root,
+ if (algo == PUBKEY_ALGO_RSA)
+ err = key_from_sexp (pk->pkey, s_key, "public-key", "ne");
+ else if (algo == PUBKEY_ALGO_ECDSA
+- || algo == PUBKEY_ALGO_EDDSA
+- || algo == PUBKEY_ALGO_ECDH )
++ || algo == PUBKEY_ALGO_EDDSA
++ || algo == PUBKEY_ALGO_ECDH )
+ err = ecckey_from_sexp (pk->pkey, s_key, algo);
+ else
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index f909c6f..e6a7698 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -1312,10 +1312,10 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
+ {
+ gpg_error_t err;
+- unsigned char *qbuf;
++ unsigned char *qbuf = NULL;
+ const unsigned char *ecc_q;
+ size_t ecc_q_len;
+- gcry_mpi_t oid;
++ gcry_mpi_t oid = NULL;
+ int n;
+ const unsigned char *oidbuf;
+ size_t oid_len;
+@@ -1338,15 +1338,16 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ if (!oidbuf)
+ {
+ err = gpg_error_from_syserror ();
+- gcry_mpi_release (oid);
+- return err;
++ goto leave;
+ }
+- gcry_mpi_release (oid);
+ oid_len = (n+7)/8;
+
+ qbuf = xtrymalloc (ecc_q_len + 1);
+ if (!qbuf)
+- return gpg_error_from_syserror ();
++ {
++ err = gpg_error_from_syserror ();
++ goto leave;
++ }
+
+ if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+ { /* Prepend 0x40 prefix. */
+@@ -1359,7 +1360,7 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+
+ if (ctrl)
+ {
+- send_key_data (ctrl, "q", ecc_q, ecc_q_len);
++ send_key_data (ctrl, "q", qbuf, ecc_q_len);
+ send_key_data (ctrl, "curve", oidbuf, oid_len);
+ }
+
+@@ -1399,6 +1400,7 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ curve = openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 1);
+ err = gcry_sexp_build (r_sexp, NULL, format, curve, (int)ecc_q_len, qbuf);
+ leave:
++ gcry_mpi_release (oid);
+ xfree (qbuf);
+ return err;
+ }
+@@ -3344,8 +3346,8 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ const char *oidstr = NULL;
+ int flag_djb_tweak = 0;
+ int algo;
+- gcry_mpi_t oid;
+- const unsigned char *oidbuf = NULL;
++ gcry_mpi_t oid = NULL;
++ const unsigned char *oidbuf;
+ unsigned int n;
+ size_t oid_len;
+ unsigned char fprbuf[20];
+@@ -3498,10 +3500,8 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ if (!oidbuf)
+ {
+ err = gpg_error_from_syserror ();
+- gcry_mpi_release (oid);
+ goto leave;
+ }
+- gcry_mpi_release (oid);
+ oid_len = (n+7)/8;
+
+ if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
+@@ -3583,6 +3583,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
+
+ leave:
++ gcry_mpi_release (oid);
+ return err;
+ }
+
diff --git a/debian/patches/0088-g10-Write-first-keybox-record-in-binary-mode.patch b/debian/patches/0088-g10-Write-first-keybox-record-in-binary-mode.patch
new file mode 100644
index 0000000..e05cf16
--- /dev/null
+++ b/debian/patches/0088-g10-Write-first-keybox-record-in-binary-mode.patch
@@ -0,0 +1,27 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Fri, 21 Oct 2016 14:59:26 +0200
+Subject: g10: Write first keybox record in binary mode
+
+* g10/keydb.c (maybe_create_keyring_or_box): Open in binary mode.
+
+--
+This fixes keybox corruption on windows.
+
+Signed-off-by: Andre Heinecke <aheinecke at intevation.de>
+---
+ g10/keydb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/g10/keydb.c b/g10/keydb.c
+index e49e25f..b959f05 100644
+--- a/g10/keydb.c
++++ b/g10/keydb.c
+@@ -415,7 +415,7 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
+ that the detection magic will work the next time it is used. */
+ if (is_box)
+ {
+- FILE *fp = fopen (filename, "w");
++ FILE *fp = fopen (filename, "wb");
+ if (!fp)
+ rc = gpg_error_from_syserror ();
+ else
diff --git a/debian/patches/0089-g10-More-card-key-generation-change.patch b/debian/patches/0089-g10-More-card-key-generation-change.patch
new file mode 100644
index 0000000..09ca3c8
--- /dev/null
+++ b/debian/patches/0089-g10-More-card-key-generation-change.patch
@@ -0,0 +1,161 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Sat, 22 Oct 2016 08:45:35 +0900
+Subject: g10: More card key generation change.
+
+* g10/keygen.c (gen_card_key): Add back ALGO as the second argument.
+Don't get ALGO by KEY-ATTR by this function. It's caller to provide
+ALGO. Don't do that by both of caller and callee.
+(generate_keypair): Only put paramerters needed. Use parameters
+for ALGO to call gen_card_key.
+(generate_card_subkeypair): Get ALGO and call gen_card_key with it.
+
+--
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ g10/keygen.c | 63 +++++++++++++++++++++++-------------------------------------
+ 1 file changed, 24 insertions(+), 39 deletions(-)
+
+diff --git a/g10/keygen.c b/g10/keygen.c
+index a59435d..61e070c 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -152,10 +152,11 @@ static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
+ u32 *r_expire,
+ unsigned int *r_nbits, char **r_curve);
+ static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+- struct output_control_s *outctrl, int card );
++ struct output_control_s *outctrl, int card );
+ static int write_keyblock (iobuf_t out, kbnode_t node);
+-static gpg_error_t gen_card_key (int keyno, int is_primary, kbnode_t pub_root,
+- u32 *timestamp, u32 expireval);
++static gpg_error_t gen_card_key (int keyno, int algo, int is_primary,
++ kbnode_t pub_root, u32 *timestamp,
++ u32 expireval);
+
+
+ static void
+@@ -255,7 +256,7 @@ keygen_add_key_expire (PKT_signature *sig, void *opaque)
+
+ buf[0] = (u >> 24) & 0xff;
+ buf[1] = (u >> 16) & 0xff;
+- buf[2] = (u >> 8) & 0xff;
++ buf[2] = (u >> 8) & 0xff;
+ buf[3] = u & 0xff;
+ build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4);
+ }
+@@ -3783,24 +3784,6 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
+ strcpy (r->u.value, "sign");
+ r->next = para;
+ para = r;
+- if (info.key_attr[0].algo == PUBKEY_ALGO_RSA)
+- {
+- r = xcalloc (1, sizeof *r + 20 );
+- r->key = pKEYLENGTH;
+- sprintf( r->u.value, "%u", info.key_attr[0].nbits);
+- r->next = para;
+- para = r;
+- }
+- else if (info.key_attr[0].algo == PUBKEY_ALGO_ECDSA
+- || info.key_attr[0].algo == PUBKEY_ALGO_EDDSA
+- || info.key_attr[0].algo == PUBKEY_ALGO_ECDH)
+- {
+- r = xcalloc (1, sizeof *r + strlen (info.key_attr[0].curve));
+- r->key = pKEYCURVE;
+- strcpy (r->u.value, info.key_attr[0].curve);
+- r->next = para;
+- para = r;
+- }
+
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pSUBKEYTYPE;
+@@ -4282,7 +4265,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+ get_parameter_passphrase (para),
+ &cache_nonce, NULL);
+ else
+- err = gen_card_key (1, 1, pub_root, ×tamp,
++ err = gen_card_key (1, get_parameter_algo( para, pKEYTYPE, NULL ),
++ 1, pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
+
+ /* Get the pointer to the generated public key packet. */
+@@ -4320,7 +4304,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+
+ if (!err && card && get_parameter (para, pAUTHKEYTYPE))
+ {
+- err = gen_card_key (3, 0, pub_root, ×tamp,
++ err = gen_card_key (3, get_parameter_algo( para, pAUTHKEYTYPE, NULL ),
++ 0, pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
+ if (!err)
+ err = write_keybinding (pub_root, pri_psk, NULL,
+@@ -4359,7 +4344,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+ }
+ else
+ {
+- err = gen_card_key (2, 0, pub_root, ×tamp,
++ err = gen_card_key (2, 0, get_parameter_algo (para, pSUBKEYTYPE, NULL),
++ pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
+ }
+
+@@ -4796,9 +4782,20 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
+ u32 cur_time;
+ struct para_data_s *para = NULL;
+ PKT_public_key *sub_pk = NULL;
++ int algo;
++ struct agent_card_info_s info;
+
+ log_assert (keyno >= 1 && keyno <= 3);
+
++ memset (&info, 0, sizeof (info));
++ err = agent_scd_getattr ("KEY-ATTR", &info);
++ if (err)
++ {
++ log_error (_("error getting current key info: %s\n"), gpg_strerror (err));
++ return err;
++ }
++ algo = info.key_attr[keyno-1].algo;
++
+ para = xtrycalloc (1, sizeof *para + strlen (serialno) );
+ if (!para)
+ {
+@@ -4857,7 +4854,7 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
+
+ /* Note, that depending on the backend, the card key generation may
+ update CUR_TIME. */
+- err = gen_card_key (keyno, 0, pub_keyblock, &cur_time, expire);
++ err = gen_card_key (keyno, algo, 0, pub_keyblock, &cur_time, expire);
+ /* Get the pointer to the generated public subkey packet. */
+ if (!err)
+ {
+@@ -4905,29 +4902,17 @@ write_keyblock( IOBUF out, KBNODE node )
+
+ /* Note that timestamp is an in/out arg. */
+ static gpg_error_t
+-gen_card_key (int keyno, int is_primary, kbnode_t pub_root,
++gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root,
+ u32 *timestamp, u32 expireval)
+ {
+ #ifdef ENABLE_CARD_SUPPORT
+ gpg_error_t err;
+- struct agent_card_info_s info;
+- int algo;
+ PACKET *pkt;
+ PKT_public_key *pk;
+ char keyid[10];
+ unsigned char *public;
+ gcry_sexp_t s_key;
+
+- memset (&info, 0, sizeof (info));
+- err = agent_scd_getattr ("KEY-ATTR", &info);
+- if (err)
+- {
+- log_error (_("error getting current key info: %s\n"), gpg_strerror (err));
+- return err;
+- }
+-
+- algo = info.key_attr[keyno-1].algo;
+-
+ snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno);
+
+ pk = xtrycalloc (1, sizeof *pk );
diff --git a/debian/patches/0090-g10-Fix-card-keygen-for-decryption.patch b/debian/patches/0090-g10-Fix-card-keygen-for-decryption.patch
new file mode 100644
index 0000000..49e11d6
--- /dev/null
+++ b/debian/patches/0090-g10-Fix-card-keygen-for-decryption.patch
@@ -0,0 +1,29 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Mon, 24 Oct 2016 07:52:40 +0900
+Subject: g10: Fix card keygen for decryption.
+
+* g10/keygen.c (do_generate_keypair): Fix arguments.
+
+--
+
+Reported-by: Grumpy
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ g10/keygen.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/g10/keygen.c b/g10/keygen.c
+index 61e070c..ed529c7 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -4344,8 +4344,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
+ }
+ else
+ {
+- err = gen_card_key (2, 0, get_parameter_algo (para, pSUBKEYTYPE, NULL),
+- pub_root, ×tamp,
++ err = gen_card_key (2, get_parameter_algo (para, pSUBKEYTYPE, NULL),
++ 0, pub_root, ×tamp,
+ get_parameter_u32 (para, pKEYEXPIRE));
+ }
+
diff --git a/debian/patches/0091-common-Fix-openpgp_is_curve_supported.patch b/debian/patches/0091-common-Fix-openpgp_is_curve_supported.patch
new file mode 100644
index 0000000..9c087f5
--- /dev/null
+++ b/debian/patches/0091-common-Fix-openpgp_is_curve_supported.patch
@@ -0,0 +1,30 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Mon, 24 Oct 2016 11:20:14 +0900
+Subject: common: Fix openpgp_is_curve_supported.
+
+* common/openpgp-oid.c (openpgp_is_curve_supported): Support both of
+canonical name of the curve and alias.
+
+--
+Only alias (the name for print) was allowed before this change.
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ common/openpgp-oid.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c
+index 7c93547..dd549e0 100644
+--- a/common/openpgp-oid.c
++++ b/common/openpgp-oid.c
+@@ -424,8 +424,8 @@ openpgp_is_curve_supported (const char *name, int *r_algo)
+ *r_algo = 0;
+ for (idx = 0; idx < DIM (oidtable) && oidtable[idx].name; idx++)
+ {
+- if (!strcmp (name, (oidtable[idx].alias? oidtable[idx].alias
+- /**/ : oidtable[idx].name))
++ if ((!strcmp (name, oidtable[idx].name)
++ || (oidtable[idx].alias && !strcmp (name, (oidtable[idx].alias))))
+ && curve_supported_p (oidtable[idx].name))
+ {
+ if (r_algo)
diff --git a/debian/patches/0092-scd-Use-canonical-curve-name-of-libgcrypt.patch b/debian/patches/0092-scd-Use-canonical-curve-name-of-libgcrypt.patch
new file mode 100644
index 0000000..edc7c42
--- /dev/null
+++ b/debian/patches/0092-scd-Use-canonical-curve-name-of-libgcrypt.patch
@@ -0,0 +1,318 @@
+From: NIIBE Yutaka <gniibe at fsij.org>
+Date: Mon, 24 Oct 2016 11:22:44 +0900
+Subject: scd: Use canonical curve name of libgcrypt.
+
+* scd/app-openpgp.c (send_key_attr): Use curve instead of OID.
+(ecdh_params): New.
+(ecc_read_pubkey): Use ecdh_params. Use curve name.
+(ecc_writekey): Likewise.
+(ecc_curve): Rename from ecc_oid.
+(parse_algorithm_attribute): Use ecc_curve.
+* g10/call-agent.c (learn_status_cb): Use openpgp_is_curve_supported to
+intern the curve name string.
+* g10/card-util.c (card_status): Conver curve name to alias for print.
+--
+Now, sdcaemon answer for KEY-ATTR is in the canonical curve name
+instead of the alias. Since it is used of key generation for
+card encryption key with backup, it should be canonical name.
+
+Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
+---
+ g10/call-agent.c | 10 +------
+ g10/card-util.c | 13 +++++++-
+ scd/app-openpgp.c | 89 ++++++++++++++++++++++++++++++++++++-------------------
+ 3 files changed, 71 insertions(+), 41 deletions(-)
+
+diff --git a/g10/call-agent.c b/g10/call-agent.c
+index e7af001..b17a80f 100644
+--- a/g10/call-agent.c
++++ b/g10/call-agent.c
+@@ -624,15 +624,7 @@ learn_status_cb (void *opaque, const char *line)
+ parm->key_attr[keyno].nbits = strtoul (line+n+3, NULL, 10);
+ else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
+ || algo == PUBKEY_ALGO_EDDSA)
+- {
+- const char *curve;
+-
+- for (i = 0; (curve = openpgp_enum_curves (&i));)
+- if (!strcmp (curve, line+n))
+- break;
+-
+- parm->key_attr[keyno].curve = curve;
+- }
++ parm->key_attr[keyno].curve = openpgp_is_curve_supported (line+n, NULL);
+ }
+ else if (keywordlen == 12 && !memcmp (keyword, "PRIVATE-DO-", 11)
+ && strchr("1234", keyword[11]))
+diff --git a/g10/card-util.c b/g10/card-util.c
+index 2f3f714..b5fe84b 100644
+--- a/g10/card-util.c
++++ b/g10/card-util.c
+@@ -568,7 +568,18 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
+ else if (info.key_attr[i].algo == PUBKEY_ALGO_ECDH
+ || info.key_attr[i].algo == PUBKEY_ALGO_ECDSA
+ || info.key_attr[i].algo == PUBKEY_ALGO_EDDSA)
+- tty_fprintf (fp, " %s", info.key_attr[i].curve);
++ {
++ const char *curve_for_print = "?";
++
++ if (info.key_attr[i].curve)
++ {
++ const char *oid;
++ oid = openpgp_curve_to_oid (info.key_attr[i].curve, NULL);
++ if (oid)
++ curve_for_print = openpgp_oid_to_curve (oid, 0);
++ }
++ tty_fprintf (fp, " %s", curve_for_print);
++ }
+ tty_fprintf (fp, "\n");
+ }
+ tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n",
+diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
+index e6a7698..4e042e7 100644
+--- a/scd/app-openpgp.c
++++ b/scd/app-openpgp.c
+@@ -228,7 +228,7 @@ struct app_local_s {
+ rsa_key_format_t format;
+ } rsa;
+ struct {
+- const char *oid;
++ const char *curve;
+ int flags;
+ } ecc;
+ };
+@@ -913,7 +913,7 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno)
+ keyno==1? PUBKEY_ALGO_ECDH :
+ (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+ PUBKEY_ALGO_EDDSA : PUBKEY_ALGO_ECDSA,
+- openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 0));
++ app->app_local->keyattr[keyno].ecc.curve);
+ }
+ else
+ snprintf (buffer, sizeof buffer, "%d 0 0 UNKNOWN", keyno+1);
+@@ -1307,6 +1307,29 @@ rsa_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ return err;
+ }
+
++
++/* Determine KDF hash algorithm and KEK encryption algorithm by CURVE. */
++static const unsigned char*
++ecdh_params (const char *curve)
++{
++ unsigned int nbits;
++
++ openpgp_curve_to_oid (curve, &nbits);
++
++ /* See RFC-6637 for those constants.
++ 0x03: Number of bytes
++ 0x01: Version for this parameter format
++ KDF algo
++ KEK algo
++ */
++ if (nbits <= 256)
++ return (const unsigned char*)"\x03\x01\x08\x07";
++ else if (nbits <= 384)
++ return (const unsigned char*)"\x03\x01\x09\x08";
++ else
++ return (const unsigned char*)"\x03\x01\x0a\x09";
++}
++
+ static gpg_error_t
+ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
+@@ -1317,11 +1340,12 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ size_t ecc_q_len;
+ gcry_mpi_t oid = NULL;
+ int n;
++ const char *curve;
++ const char *oidstr;
+ const unsigned char *oidbuf;
+ size_t oid_len;
+ int algo;
+ const char *format;
+- const char *curve;
+
+ ecc_q = find_tlv (data, datalen, 0x0086, &ecc_q_len);
+ if (!ecc_q)
+@@ -1330,10 +1354,11 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+- err = openpgp_oid_from_str (app->app_local->keyattr[keyno].ecc.oid, &oid);
++ curve = app->app_local->keyattr[keyno].ecc.curve;
++ oidstr = openpgp_curve_to_oid (curve, NULL);
++ err = openpgp_oid_from_str (oidstr, &oid);
+ if (err)
+ return err;
+-
+ oidbuf = gcry_mpi_get_opaque (oid, &n);
+ if (!oidbuf)
+ {
+@@ -1367,7 +1392,7 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ if (keyno == 1)
+ {
+ if (ctrl)
+- send_key_data (ctrl, "kdf", "\x03\x01\x08\x07", (size_t)4);
++ send_key_data (ctrl, "kdf/kek", ecdh_params (curve), (size_t)4);
+ algo = PUBKEY_ALGO_ECDH;
+ }
+ else
+@@ -1383,7 +1408,7 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ unsigned char fprbuf[20];
+
+ err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+- qbuf, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
++ qbuf, ecc_q_len, ecdh_params (curve), (size_t)4);
+ if (err)
+ goto leave;
+
+@@ -1397,8 +1422,9 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ else
+ format = "(public-key(ecc(curve%s)(flags eddsa)(q%b)))";
+
+- curve = openpgp_oid_to_curve (app->app_local->keyattr[keyno].ecc.oid, 1);
+- err = gcry_sexp_build (r_sexp, NULL, format, curve, (int)ecc_q_len, qbuf);
++ err = gcry_sexp_build (r_sexp, NULL, format,
++ app->app_local->keyattr[keyno].ecc.curve,
++ (int)ecc_q_len, qbuf);
+ leave:
+ gcry_mpi_release (oid);
+ xfree (qbuf);
+@@ -3342,8 +3368,9 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ const unsigned char *ecc_q = NULL;
+ const unsigned char *ecc_d = NULL;
+ size_t ecc_q_len, ecc_d_len;
++ const char *curve = NULL;
+ u32 created_at = 0;
+- const char *oidstr = NULL;
++ const char *oidstr;
+ int flag_djb_tweak = 0;
+ int algo;
+ gcry_mpi_t oid = NULL;
+@@ -3372,22 +3399,22 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+
+ if (tok && toklen == 5 && !memcmp (tok, "curve", 5))
+ {
+- unsigned char *curve;
++ char *curve_name;
+
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+
+- curve = xtrymalloc (toklen+1);
+- if (!curve)
++ curve_name = xtrymalloc (toklen+1);
++ if (!curve_name)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+- memcpy (curve, tok, toklen);
+- curve[toklen] = 0;
+- oidstr = openpgp_curve_to_oid (curve, NULL);
+- xfree (curve);
++ memcpy (curve_name, tok, toklen);
++ curve_name[toklen] = 0;
++ curve = openpgp_is_curve_supported (curve_name, NULL);
++ xfree (curve_name);
+ }
+ else if (tok && toklen == 5 && !memcmp (tok, "flags", 5))
+ {
+@@ -3474,7 +3501,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+
+ /* Check that we have all parameters and that they match the card
+ description. */
+- if (!oidstr)
++ if (!curve)
+ {
+ log_error (_("unsupported curve\n"));
+ err = gpg_error (GPG_ERR_INV_VALUE);
+@@ -3493,6 +3520,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ else
+ algo = PUBKEY_ALGO_ECDSA;
+
++ oidstr = openpgp_curve_to_oid (curve, NULL);
+ err = openpgp_oid_from_str (oidstr, &oid);
+ if (err)
+ goto leave;
+@@ -3505,7 +3533,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ oid_len = (n+7)/8;
+
+ if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
+- || app->app_local->keyattr[keyno].ecc.oid != oidstr
++ || app->app_local->keyattr[keyno].ecc.curve != curve
+ || (flag_djb_tweak !=
+ (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)))
+ {
+@@ -3580,7 +3608,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ }
+
+ err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+- ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
++ ecc_q, ecc_q_len, ecdh_params (curve), (size_t)4);
+
+ leave:
+ gcry_mpi_release (oid);
+@@ -4578,12 +4606,11 @@ parse_historical (struct app_local_s *apploc,
+
+ /*
+ * Check if the OID in an DER encoding is available by GnuPG/libgcrypt,
+- * and return the constant string in dotted decimal form.
+- * Return NULL if not available.
++ * and return the curve name. Return NULL if not available.
+ * The constant string is not allocated dynamically, never free it.
+ */
+ static const char *
+-ecc_oid (unsigned char *buf, size_t buflen)
++ecc_curve (unsigned char *buf, size_t buflen)
+ {
+ gcry_mpi_t oid;
+ char *oidstr;
+@@ -4608,7 +4635,7 @@ ecc_oid (unsigned char *buf, size_t buflen)
+ if (!oidstr)
+ return NULL;
+
+- result = openpgp_curve_to_oid (oidstr, NULL);
++ result = openpgp_oid_to_curve (oidstr, 1);
+ xfree (oidstr);
+ return result;
+ }
+@@ -4671,7 +4698,7 @@ parse_algorithm_attribute (app_t app, int keyno)
+ else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA
+ || *buffer == PUBKEY_ALGO_EDDSA)
+ {
+- const char *oid;
++ const char *curve;
+ int oidlen = buflen - 1;
+
+ app->app_local->keyattr[keyno].ecc.flags = 0;
+@@ -4683,22 +4710,22 @@ parse_algorithm_attribute (app_t app, int keyno)
+ app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_PUBKEY;
+ }
+
+- oid = ecc_oid (buffer + 1, oidlen);
++ curve = ecc_curve (buffer + 1, oidlen);
+
+- if (!oid)
++ if (!curve)
+ log_printhex ("Curve with OID not supported: ", buffer+1, buflen-1);
+ else
+ {
+ app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
+- app->app_local->keyattr[keyno].ecc.oid = oid;
++ app->app_local->keyattr[keyno].ecc.curve = curve;
+ if (*buffer == PUBKEY_ALGO_EDDSA
+ || (*buffer == PUBKEY_ALGO_ECDH
+- && !strcmp (app->app_local->keyattr[keyno].ecc.oid,
+- "1.3.6.1.4.1.3029.1.5.1")))
++ && !strcmp (app->app_local->keyattr[keyno].ecc.curve,
++ "Curve25519")))
+ app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_DJB_TWEAK;
+ if (opt.verbose)
+ log_printf
+- ("ECC, curve=%s%s\n", app->app_local->keyattr[keyno].ecc.oid,
++ ("ECC, curve=%s%s\n", app->app_local->keyattr[keyno].ecc.curve,
+ !(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+ "": keyno==1? " (djb-tweak)": " (eddsa)");
+ }
diff --git a/debian/patches/0093-agent-Slightly-change-structure-of-cmd_readkey.patch b/debian/patches/0093-agent-Slightly-change-structure-of-cmd_readkey.patch
new file mode 100644
index 0000000..2a57606
--- /dev/null
+++ b/debian/patches/0093-agent-Slightly-change-structure-of-cmd_readkey.patch
@@ -0,0 +1,117 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Mon, 24 Oct 2016 12:55:21 +0200
+Subject: agent: Slightly change structure of cmd_readkey.
+
+* agent/command.c (cmd_readkey): Avoid a leave label in the middle of
+the code. Remove the special return.
+--
+
+This helps to get better debug output.
+
+The set_error macro which is used by parse_keygrip merely sets the
+error code into the Assuan context. It is thus no problem anymore to
+call leave_cmd after having used set_error. This might havve been
+diffferent in the past.
+
+Signed-off-by: Werner Koch <wk at gnupg.org>
+---
+ agent/command.c | 60 +++++++++++++++++++++++++++++----------------------------
+ 1 file changed, 31 insertions(+), 29 deletions(-)
+
+diff --git a/agent/command.c b/agent/command.c
+index b17c62d..ba9fdf7 100644
+--- a/agent/command.c
++++ b/agent/command.c
+@@ -384,7 +384,9 @@ progress_cb (ctrl_t ctrl, const char *what, int printchar,
+ }
+
+
+-/* Helper to print a message while leaving a command. */
++/* Helper to print a message while leaving a command. Note that this
++ * function does not call assuan_set_error; the caller may do this
++ * prior to calling us. */
+ static gpg_error_t
+ leave_cmd (assuan_context_t ctx, gpg_error_t err)
+ {
+@@ -993,17 +995,19 @@ cmd_readkey (assuan_context_t ctx, char *line)
+ unsigned char grip[20];
+ gcry_sexp_t s_pkey = NULL;
+ unsigned char *pkbuf = NULL;
++ char *serialno = NULL;
+ size_t pkbuflen;
++ const char *opt_card;
+
+ if (ctrl->restricted)
+ return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
+- if (has_option_name (line, "--card"))
+- {
+- const char *keyid;
+- char *serialno = NULL;
++ opt_card = has_option_name (line, "--card");
++ line = skip_options (line);
+
+- keyid = skip_options (line);
++ if (opt_card)
++ {
++ const char *keyid = opt_card;
+
+ rc = agent_card_getattr (ctrl, "SERIALNO", &serialno);
+ if (rc)
+@@ -1035,35 +1039,33 @@ cmd_readkey (assuan_context_t ctx, char *line)
+ goto leave;
+
+ rc = assuan_send_data (ctx, pkbuf, pkbuflen);
+-
+- leave:
+- xfree (serialno);
+- xfree (pkbuf);
+- gcry_sexp_release (s_pkey);
+- return leave_cmd (ctx, rc);
+ }
+-
+- rc = parse_keygrip (ctx, line, grip);
+- if (rc)
+- return rc; /* Return immediately as this is already an Assuan error code.*/
+-
+- rc = agent_public_key_from_file (ctrl, grip, &s_pkey);
+- if (!rc)
++ else
+ {
+- pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+- assert (pkbuflen);
+- pkbuf = xtrymalloc (pkbuflen);
+- if (!pkbuf)
+- rc = gpg_error_from_syserror ();
+- else
++ rc = parse_keygrip (ctx, line, grip);
++ if (rc)
++ goto leave;
++
++ rc = agent_public_key_from_file (ctrl, grip, &s_pkey);
++ if (!rc)
+ {
+- gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen);
+- rc = assuan_send_data (ctx, pkbuf, pkbuflen);
+- xfree (pkbuf);
++ pkbuflen = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
++ log_assert (pkbuflen);
++ pkbuf = xtrymalloc (pkbuflen);
++ if (!pkbuf)
++ rc = gpg_error_from_syserror ();
++ else
++ {
++ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, pkbuflen);
++ rc = assuan_send_data (ctx, pkbuf, pkbuflen);
++ }
+ }
+- gcry_sexp_release (s_pkey);
+ }
+
++ leave:
++ xfree (serialno);
++ xfree (pkbuf);
++ gcry_sexp_release (s_pkey);
+ return leave_cmd (ctx, rc);
+ }
+
diff --git a/debian/patches/0094-agent-Minor-cleanup-for-recent-change-in-findkey.c.patch b/debian/patches/0094-agent-Minor-cleanup-for-recent-change-in-findkey.c.patch
new file mode 100644
index 0000000..3005c22
--- /dev/null
+++ b/debian/patches/0094-agent-Minor-cleanup-for-recent-change-in-findkey.c.patch
@@ -0,0 +1,32 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Mon, 24 Oct 2016 13:01:06 +0200
+Subject: agent: Minor cleanup for recent change in findkey.c
+
+* agent/findkey.c (agent_write_private_key): Avoid label name error.
+
+Signed-off-by: Werner Koch <wk at gnupg.org>
+---
+ agent/findkey.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+diff --git a/agent/findkey.c b/agent/findkey.c
+index 162e8c2..c67dc72 100644
+--- a/agent/findkey.c
++++ b/agent/findkey.c
+@@ -157,14 +157,10 @@ agent_write_private_key (const unsigned char *grip,
+ {
+ fp = es_fopen (fname, "wbx,mode=-rw");
+ if (!fp)
+- {
+- tmperr = gpg_error_from_syserror ();
+- goto error;
+- }
++ tmperr = gpg_error_from_syserror ();
+ }
+- else
++ if (!fp)
+ {
+- error:
+ log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr));
+ xfree (fname);
+ return tmperr;
diff --git a/debian/patches/0095-gpg-Replace-two-sprintf-calls.patch b/debian/patches/0095-gpg-Replace-two-sprintf-calls.patch
new file mode 100644
index 0000000..7e0d5c3
--- /dev/null
+++ b/debian/patches/0095-gpg-Replace-two-sprintf-calls.patch
@@ -0,0 +1,54 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Mon, 24 Oct 2016 13:12:05 +0200
+Subject: gpg: Replace two sprintf calls.
+
+* g10/keygen.c (print_status_key_created): Use snprintf for now.
+(ask_expire_interval): Replace xmalloc and sprintf by xasprintf.
+--
+
+Future updates: Replace code like
+
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pKEYLENGTH;
+ sprintf( r->u.value, "%u", info.key_attr[0].nbits);
+
+by something like
+
+ r = new_r_with_value ("%u", info.key_attr[0].nbits);
+ r->key = pKEYLENGTH;
+
+Signed-off-by: Werner Koch <wk at gnupg.org>
+---
+ g10/keygen.c | 11 +++--------
+ 1 file changed, 3 insertions(+), 8 deletions(-)
+
+diff --git a/g10/keygen.c b/g10/keygen.c
+index ed529c7..d98b70b 100644
+--- a/g10/keygen.c
++++ b/g10/keygen.c
+@@ -180,8 +180,9 @@ print_status_key_created (int letter, PKT_public_key *pk, const char *handle)
+ *p++ = ' ';
+ fingerprint_from_pk (pk, array, &n);
+ s = array;
++ /* Fixme: Use bin2hex */
+ for (i=0; i < n ; i++, s++, p += 2)
+- sprintf (p, "%02X", *s);
++ snprintf (p, 3, "%02X", *s);
+ }
+ }
+ if (*handle)
+@@ -2428,13 +2429,7 @@ ask_expire_interval(int object,const char *def_expire)
+ {
+ char *prompt;
+
+-#define PROMPTSTRING _("Signature is valid for? (%s) ")
+- /* This will actually end up larger than necessary because
+- of the 2 bytes for '%s' */
+- prompt=xmalloc(strlen(PROMPTSTRING)+strlen(def_expire)+1);
+- sprintf(prompt,PROMPTSTRING,def_expire);
+-#undef PROMPTSTRING
+-
++ prompt = xasprintf (_("Signature is valid for? (%s) "), def_expire);
+ answer = cpr_get("siggen.valid",prompt);
+ xfree(prompt);
+
diff --git a/debian/patches/0096-agent-tests-w32-Fix-relaying-pinentry-user-data-fix-.patch b/debian/patches/0096-agent-tests-w32-Fix-relaying-pinentry-user-data-fix-.patch
new file mode 100644
index 0000000..a730f2f
--- /dev/null
+++ b/debian/patches/0096-agent-tests-w32-Fix-relaying-pinentry-user-data-fix-.patch
@@ -0,0 +1,190 @@
+From: Justus Winter <justus at g10code.com>
+Date: Tue, 25 Oct 2016 17:07:08 +0200
+Subject: agent, tests, w32: Fix relaying pinentry user data,
+ fix fake-pinentry.
+
+* agent/call-pinentry.c (start_pinentry): Also send the user data
+using an Assuan 'OPTION' command.
+* tests/openpgp/fake-pinentry.c (get_passphrase): Fix updating
+passphrase file.
+(spacep): Include newline characters.
+(rstrip): New function.
+(main): Handle Windows line endings. Handle the userdata option, and
+restart with the new options.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+---
+ agent/call-pinentry.c | 13 +++++++++
+ tests/openpgp/fake-pinentry.c | 65 ++++++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 71 insertions(+), 7 deletions(-)
+
+diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c
+index 46db9e8..813df9a 100644
+--- a/agent/call-pinentry.c
++++ b/agent/call-pinentry.c
+@@ -354,6 +354,19 @@ start_pinentry (ctrl_t ctrl)
+ if (DBG_IPC)
+ log_debug ("connection to PIN entry established\n");
+
++ value = session_env_getenv (ctrl->session_env, "PINENTRY_USER_DATA");
++ if (value != NULL)
++ {
++ char *optstr;
++ if (asprintf (&optstr, "OPTION pinentry-user-data=%s", value) < 0 )
++ return unlock_pinentry (out_of_core ());
++ rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
++ NULL);
++ xfree (optstr);
++ if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION)
++ return unlock_pinentry (rc);
++ }
++
+ rc = assuan_transact (entry_ctx,
+ opt.no_grab? "OPTION no-grab":"OPTION grab",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+diff --git a/tests/openpgp/fake-pinentry.c b/tests/openpgp/fake-pinentry.c
+index 6ef6126..baa79a8 100644
+--- a/tests/openpgp/fake-pinentry.c
++++ b/tests/openpgp/fake-pinentry.c
+@@ -18,10 +18,12 @@
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
++#include <errno.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdarg.h>
++#include <unistd.h>
+
+ FILE *log_stream;
+
+@@ -108,12 +110,39 @@ get_passphrase (const char *fname)
+
+ fclose (source);
+ fclose (sink);
+- rename (fname_new, fname);
++ if (unlink (fname))
++ {
++ fprintf (stderr, "Failed to remove %s: %s",
++ fname, strerror (errno));
++ exit (1);
++ }
++
++ if (rename (fname_new, fname))
++ {
++ fprintf (stderr, "Failed to rename %s to %s: %s",
++ fname, fname_new, strerror (errno));
++ exit (1);
++ }
+ return passphrase;
+ }
+
+
+-#define spacep(p) (*(p) == ' ' || *(p) == '\t')
++#define spacep(p) (*(p) == ' ' || *(p) == '\t' \
++ || *(p) == '\r' || *(p) == '\n')
++
++/* rstrip line. */
++void
++rstrip (char *buffer)
++{
++ char *p;
++ for (p = buffer + strlen (buffer) - 1; p >= buffer; p--)
++ {
++ if (! spacep (p))
++ break;
++ *p = 0;
++ }
++}
++
+
+ /* Skip over options in LINE.
+
+@@ -164,6 +193,8 @@ int
+ main (int argc, char **argv)
+ {
+ char *args;
++ char *option_user_data = NULL;
++ int got_environment_user_data;
+ char *logfile;
+ char *passphrasefile;
+ char *passphrase;
+@@ -175,9 +206,11 @@ main (int argc, char **argv)
+ setvbuf (stdout, NULL, _IOLBF, BUFSIZ);
+
+ args = getenv ("PINENTRY_USER_DATA");
++ got_environment_user_data = args != NULL;
+ if (! args)
+ args = "";
+
++ restart:
+ logfile = option_value (args, "--logfile");
+ if (logfile)
+ {
+@@ -214,9 +247,7 @@ main (int argc, char **argv)
+ return 1;
+ }
+
+- p = passphrase + strlen (passphrase) - 1;
+- if (*p == '\n')
+- *p = 0;
++ rstrip (passphrase);
+ }
+ else
+ {
+@@ -225,12 +256,13 @@ main (int argc, char **argv)
+ passphrase = "no PINENTRY_USER_DATA -- using default passphrase";
+ }
+
+- reply ("# fake-pinentry started. Passphrase='%s'.\n", passphrase);
++ reply ("# fake-pinentry(%d) started. Passphrase='%s'.\n",
++ getpid (), passphrase);
+ reply ("OK - what's up?\n");
+
+ while (! feof (stdin))
+ {
+- char buffer[1024];
++ char buffer[1024], *p;
+
+ if (fgets (buffer, sizeof buffer, stdin) == NULL)
+ break;
+@@ -238,6 +270,8 @@ main (int argc, char **argv)
+ if (log_stream)
+ fprintf (log_stream, "< %s", buffer);
+
++ rstrip (buffer);
++
+ if (strncmp (buffer, "GETPIN", 6) == 0)
+ reply ("D %s\n", passphrase);
+ else if (strncmp (buffer, "BYE", 3) == 0)
+@@ -245,6 +279,22 @@ main (int argc, char **argv)
+ reply ("OK\n");
+ break;
+ }
++#define OPT_USER_DATA "OPTION pinentry-user-data="
++ else if (strncmp (buffer, OPT_USER_DATA, strlen (OPT_USER_DATA)) == 0)
++ {
++ if (got_environment_user_data)
++ {
++ reply ("OK - I already got the data from the environment.\n");
++ continue;
++ }
++
++ if (log_stream)
++ fclose (log_stream);
++ log_stream = NULL;
++ free (option_user_data);
++ option_user_data = args = strdup (buffer + strlen (OPT_USER_DATA));
++ goto restart;
++ }
+
+ reply ("OK\n");
+ }
+@@ -253,5 +303,6 @@ main (int argc, char **argv)
+ if (log_stream)
+ fclose (log_stream);
+
++ free (option_user_data);
+ return 0;
+ }
diff --git a/debian/patches/series b/debian/patches/series
index 0de45be..de68121 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -61,3 +61,36 @@
0061-common-Avoid-pointer-arithmetic-on-string-literals.patch
0062-agent-dirmngr-scd-Fix-init_common_subsystems.patch
0063-agent-Fix-get_socket_name.patch
+0064-tools-Fix-error-handling.patch
+0065-g10-Fix-a-column-s-type-in-TOFU-DB.patch
+0066-agent-Move-inotify-code-to-common-and-improve-it.patch
+0067-agent-Use-straightforward-names-for-the-default-sock.patch
+0068-gpgconf-Fix-for-homedir.patch
+0069-scd-Fix-keytocard-for-ECC.patch
+0070-doc-Point-gpg-agent-1-at-the-right-gpg-manpage-in-SE.patch
+0071-doc-Document-how-to-manually-shut-down-gpg-agent.patch
+0072-scd-minor-cleanup-to-merge-other-works.patch
+0073-scd-Support-ECC-key-generation.patch
+0074-common-w32-Make-use-of-default_errsource-in-exechelp.patch
+0075-common-w32-Extend-gnupg_create_inbound_pipe-et-al.patch
+0076-common-w32-Communicate-with-child-in-non-blocking-mo.patch
+0077-common-Fix-copying-data-to-estreams.patch
+0078-agent-Add-card-option-for-READKEY.patch
+0079-g10-smartcard-keygen-change.patch
+0080-scd-GENKEY-updates-the-public-key-in-APP.patch
+0081-agent-g10-Fix-keygen.patch
+0082-agent-Fix-saving-with-FORCE-1.patch
+0083-Fix-use-cases-of-snprintf.patch
+0084-g10-Support-ECC-for-gen_card_key.patch
+0085-g10-Don-t-ask-keysize-for-for-non-RSA-card.patch
+0086-scd-Fix-segfault-changing-key-attr.patch
+0087-g10-scd-Fix-ECC-keygen.patch
+0088-g10-Write-first-keybox-record-in-binary-mode.patch
+0089-g10-More-card-key-generation-change.patch
+0090-g10-Fix-card-keygen-for-decryption.patch
+0091-common-Fix-openpgp_is_curve_supported.patch
+0092-scd-Use-canonical-curve-name-of-libgcrypt.patch
+0093-agent-Slightly-change-structure-of-cmd_readkey.patch
+0094-agent-Minor-cleanup-for-recent-change-in-findkey.c.patch
+0095-gpg-Replace-two-sprintf-calls.patch
+0096-agent-tests-w32-Fix-relaying-pinentry-user-data-fix-.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/gnupg2.git
More information about the Pkg-gnupg-commit
mailing list