summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2009-03-06 18:31:27 +0100
committerWerner Koch <wk@gnupg.org>2009-03-06 18:31:27 +0100
commita9c317a95c440a083809346d61cdb78abff71b12 (patch)
tree6f5199efe8fba5473afc346f003abe74f6ab424e
parentNew PIN Callback attributes in gpg-agent. (diff)
downloadgnupg2-a9c317a95c440a083809346d61cdb78abff71b12.tar.xz
gnupg2-a9c317a95c440a083809346d61cdb78abff71b12.zip
New gpg-agent command to list key information.
Gpgsm does now print the S/N of cards. Consider ephemeral keys during listing an export.
-rw-r--r--NEWS6
-rw-r--r--agent/ChangeLog15
-rw-r--r--agent/agent.h5
-rw-r--r--agent/command.c161
-rw-r--r--agent/divert-scd.c40
-rw-r--r--agent/findkey.c130
-rw-r--r--agent/protect.c67
-rw-r--r--common/ChangeLog4
-rw-r--r--common/sexputil.c38
-rw-r--r--common/util.h4
-rw-r--r--doc/gpgsm.texi14
-rw-r--r--kbx/keybox-search.c2
-rw-r--r--scd/ChangeLog7
-rw-r--r--scd/app-nks.c133
-rw-r--r--sm/ChangeLog13
-rw-r--r--sm/call-agent.c67
-rw-r--r--sm/export.c22
-rw-r--r--sm/fingerprint.c4
-rw-r--r--sm/gpgsm.h2
-rw-r--r--sm/keylist.c41
20 files changed, 598 insertions, 177 deletions
diff --git a/NEWS b/NEWS
index afeba6861..bfa343bb4 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
Noteworthy changes in version 2.0.12
-------------------------------------------------
+ * GPGSM now always lists ephemeral certificates if specified by
+ fingerprint or keygrip.
+
+ * New command "KEYINFO" for GPG_AGENT. GPGSM now also returns
+ information about smartcards.
+
Noteworthy changes in version 2.0.11 (2009-03-03)
-------------------------------------------------
diff --git a/agent/ChangeLog b/agent/ChangeLog
index e016377a1..cae70f3bb 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,16 @@
+2009-03-06 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_keyinfo): New command.
+ (register_commands): Register it.
+ (agent_write_status): Make sure not to print LR or CR.
+ * divert-scd.c (ask_for_card): Factor shadow info parsing out to ...
+ * protect.c (parse_shadow_info): New.
+ * findkey.c (agent_key_from_file): Use make_canon_sexp.
+ (agent_write_private_key, unprotect, read_key_file)
+ (agent_key_available): Use bin2hex.
+ (agent_key_info_from_file): New.
+ (read_key_file): Log no error message for ENOENT.
+
2009-03-05 Werner Koch <wk@g10code.com>
* divert-scd.c (getpin_cb): Support flag 'P'. Change max_digits
@@ -2227,7 +2240,7 @@ Fri Aug 18 14:27:14 CEST 2000 Werner Koch <wk@openit.de>
Copyright 2001, 2002, 2003, 2004, 2005,
- 2007 Free Software Foundation, Inc.
+ 2007, 2008, 2009 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
diff --git a/agent/agent.h b/agent/agent.h
index 0e2cc9f40..fa2c61d06 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -233,6 +233,9 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
const unsigned char *grip,
gcry_sexp_t *result);
int agent_key_available (const unsigned char *grip);
+gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
+ int *r_keytype,
+ unsigned char **r_shadow_info);
/*-- call-pinentry.c --*/
void initialize_module_call_pinentry (void);
@@ -294,6 +297,8 @@ int agent_shadow_key (const unsigned char *pubkey,
unsigned char **result);
int agent_get_shadow_info (const unsigned char *shadowkey,
unsigned char const **shadow_info);
+gpg_error_t parse_shadow_info (const unsigned char *shadow_info,
+ char **r_hexsn, char **r_idstr);
/*-- trustlist.c --*/
diff --git a/agent/command.c b/agent/command.c
index 9ebcd091f..ba0f8fc4c 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1,6 +1,6 @@
/* command.c - gpg-agent command handler
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
- * 2006, 2008 Free Software Foundation, Inc.
+ * 2006, 2008, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -30,6 +30,9 @@
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
#include <assuan.h>
@@ -308,8 +311,21 @@ agent_write_status (ctrl_t ctrl, const char *keyword, ...)
*p++ = ' ';
n++;
}
- for ( ; *text && n < DIM (buf)-2; n++)
- *p++ = *text++;
+ for ( ; *text && n < DIM (buf)-3; n++, text++)
+ {
+ if (*text == '\n')
+ {
+ *p++ = '\\';
+ *p++ = 'n';
+ }
+ else if (*text == '\r')
+ {
+ *p++ = '\\';
+ *p++ = 'r';
+ }
+ else
+ *p++ = *text;
+ }
}
*p = 0;
err = assuan_write_status (ctx, keyword, buf);
@@ -806,7 +822,145 @@ cmd_readkey (assuan_context_t ctx, char *line)
}
+
+/* KEYINFO [--list] <keygrip>
+
+ Return information about the key specified by the KEYGRIP. If the
+ key is not available GPG_ERR_NOT_FOUND is returned. If the option
+ --list is given the keygrip is ignored and information about all
+ available keys are returned. The information is returned as a
+ status line with this format:
+
+ KEYINFO <keygrip> <type> <serialno> <idstr>
+
+ KEYGRIP is the keygrip.
+
+ TYPE is describes the type of the key:
+ 'D' - Regular key stored on disk,
+ 'T' - Key is stored on a smartcard (token).
+ '-' - Unknown type.
+
+ SERIALNO is an ASCII string with the serial number of the
+ smartcard. If the serial number is not known a single
+ dash '-' is used instead.
+
+ IDSTR is the IDSTR used to distinguish keys on a smartcard. If it
+ is not known a dash is used instead.
+
+ More information may be added in the future.
+*/
+static gpg_error_t
+do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip)
+{
+ gpg_error_t err;
+ char hexgrip[40+1];
+ int keytype;
+ unsigned char *shadow_info = NULL;
+ char *serialno = NULL;
+ char *idstr = NULL;
+ const char *keytypestr;
+
+ err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info);
+ if (err)
+ goto leave;
+
+ /* Reformat the grip so that we use uppercase as good style. */
+ bin2hex (grip, 20, hexgrip);
+
+ if (keytype == PRIVATE_KEY_CLEAR
+ || keytype == PRIVATE_KEY_PROTECTED)
+ keytypestr = "D";
+ else if (keytype == PRIVATE_KEY_SHADOWED)
+ keytypestr = "T";
+ else
+ keytypestr = "-";
+
+ if (shadow_info)
+ {
+ err = parse_shadow_info (shadow_info, &serialno, &idstr);
+ if (err)
+ goto leave;
+ }
+
+ err = agent_write_status (ctrl, "KEYINFO",
+ hexgrip,
+ keytypestr,
+ serialno? serialno : "-",
+ idstr? idstr : "-",
+ NULL);
+ leave:
+ xfree (shadow_info);
+ xfree (serialno);
+ xfree (idstr);
+ return err;
+}
+
+
+static int
+cmd_keyinfo (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int err;
+ unsigned char grip[20];
+ DIR *dir = NULL;
+ int list_mode;
+
+ list_mode = has_option (line, "--list");
+ line = skip_options (line);
+
+ if (list_mode)
+ {
+ char *dirname;
+ struct dirent *dir_entry;
+ char hexgrip[41];
+
+ dirname = make_filename_try (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL);
+ if (!dirname)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ dir = opendir (dirname);
+ if (!dir)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (dirname);
+ goto leave;
+ }
+ xfree (dirname);
+ while ( (dir_entry = readdir (dir)) )
+ {
+ if (strlen (dir_entry->d_name) != 44
+ || strcmp (dir_entry->d_name + 40, ".key"))
+ continue;
+ strncpy (hexgrip, dir_entry->d_name, 40);
+ hexgrip[40] = 0;
+
+ if ( hex2bin (hexgrip, grip, 20) < 0 )
+ continue; /* Bad hex string. */
+
+ err = do_one_keyinfo (ctrl, grip);
+ if (err)
+ goto leave;
+ }
+ err = 0;
+ }
+ else
+ {
+ err = parse_keygrip (ctx, line, grip);
+ if (err)
+ goto leave;
+ err = do_one_keyinfo (ctrl, grip);
+ }
+
+ leave:
+ if (dir)
+ closedir (dir);
+ if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ log_error ("command keyinfo failed: %s\n", gpg_strerror (err));
+ return err;
+}
@@ -1574,6 +1728,7 @@ register_commands (assuan_context_t ctx)
{ "GETEVENTCOUNTER",cmd_geteventcounter },
{ "ISTRUSTED", cmd_istrusted },
{ "HAVEKEY", cmd_havekey },
+ { "KEYINFO", cmd_keyinfo },
{ "SIGKEY", cmd_sigkey },
{ "SETKEY", cmd_sigkey },
{ "SETKEYDESC", cmd_setkeydesc },
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index a583f1a61..fd8c28b66 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -28,16 +28,14 @@
#include <sys/stat.h>
#include "agent.h"
-#include "sexp-parse.h"
#include "i18n.h"
+#include "sexp-parse.h"
static int
ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
{
int rc, i;
- const unsigned char *s;
- size_t n;
char *serialno;
int no_card = 0;
char *desc;
@@ -45,39 +43,19 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
int want_sn_displen;
*r_kid = NULL;
- s = shadow_info;
- if (*s != '(')
- return gpg_error (GPG_ERR_INV_SEXP);
- s++;
- n = snext (&s);
- if (!n)
- return gpg_error (GPG_ERR_INV_SEXP);
- want_sn = xtrymalloc (n*2+1);
- if (!want_sn)
- return out_of_core ();
- for (i=0; i < n; i++)
- sprintf (want_sn+2*i, "%02X", s[i]);
- s += n;
+
+ rc = parse_shadow_info (shadow_info, &want_sn, &want_kid);
+ if (rc)
+ return rc;
+
/* We assume that a 20 byte serial number is a standard one which
- seems to have the property to have a zero in the last nibble. We
- don't display this '0' because it may confuse the user */
+ has the property to have a zero in the last nibble (Due to BCD
+ representation). We don't display this '0' because it may
+ confuse the user. */
want_sn_displen = strlen (want_sn);
if (want_sn_displen == 20 && want_sn[19] == '0')
want_sn_displen--;
- n = snext (&s);
- if (!n)
- return gpg_error (GPG_ERR_INV_SEXP);
- want_kid = xtrymalloc (n+1);
- if (!want_kid)
- {
- gpg_error_t tmperr = out_of_core ();
- xfree (want_sn);
- return tmperr;
- }
- memcpy (want_kid, s, n);
- want_kid[n] = 0;
-
for (;;)
{
rc = agent_card_serialno (ctrl, &serialno);
diff --git a/agent/findkey.c b/agent/findkey.c
index 0bb6afdcb..5bea198dc 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -56,14 +56,12 @@ int
agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force)
{
- int i;
char *fname;
FILE *fp;
char hexgrip[40+4+1];
int fd;
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
+ bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
@@ -307,14 +305,12 @@ unprotect (ctrl_t ctrl, const char *desc_text,
{
struct pin_entry_info_s *pi;
struct try_unprotect_arg_s arg;
- int rc, i;
+ int rc;
unsigned char *result;
size_t resultlen;
char hexgrip[40+1];
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
- hexgrip[40] = 0;
+ bin2hex (grip, 20, hexgrip);
/* First try to get it from the cache - if there is none or we can't
unprotect it, we fall back to ask the user */
@@ -425,7 +421,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
static gpg_error_t
read_key_file (const unsigned char *grip, gcry_sexp_t *result)
{
- int i, rc;
+ int rc;
char *fname;
FILE *fp;
struct stat st;
@@ -436,8 +432,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
*result = NULL;
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
+ bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
@@ -445,7 +440,8 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
if (!fp)
{
rc = gpg_error_from_syserror ();
- log_error ("can't open `%s': %s\n", fname, strerror (errno));
+ if (gpg_err_code (rc) != GPG_ERR_ENOENT)
+ log_error ("can't open `%s': %s\n", fname, strerror (errno));
xfree (fname);
return rc;
}
@@ -488,11 +484,11 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
/* Return the secret key as an S-Exp in RESULT after locating it using
- the grip. Returns NULL in RESULT if the operation should be
- diverted to a token; SHADOW_INFO will point then to an allocated
- S-Expression with the shadow_info part from the file. CACHE_MODE
- defines now the cache shall be used. DESC_TEXT may be set to
- present a custom description for the pinentry. */
+ the GRIP. Stores NULL at RESULT if the operation shall be diverted
+ to a token; in this case an allocated S-expression with the
+ shadow_info part from the file is stored at SHADOW_INFO.
+ CACHE_MODE defines now the cache shall be used. DESC_TEXT may be
+ set to present a custom description for the pinentry. */
gpg_error_t
agent_key_from_file (ctrl_t ctrl, const char *desc_text,
const unsigned char *grip, unsigned char **shadow_info,
@@ -513,20 +509,11 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text,
return rc;
/* For use with the protection functions we also need the key as an
- canonical encoded S-expression in abuffer. Create this buffer
+ canonical encoded S-expression in a buffer. Create this buffer
now. */
- len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
- assert (len);
- buf = xtrymalloc (len);
- if (!buf)
- {
- rc = gpg_error_from_syserror ();
- gcry_sexp_release (s_skey);
- return rc;
- }
- len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len);
- assert (len);
-
+ rc = make_canon_sexp (s_skey, &buf, &len);
+ if (rc)
+ return rc;
switch (agent_private_key_type (buf))
{
@@ -842,19 +829,94 @@ agent_public_key_from_file (ctrl_t ctrl,
int
agent_key_available (const unsigned char *grip)
{
- int i;
+ int result;
char *fname;
char hexgrip[40+4+1];
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
+ bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
- i = !access (fname, R_OK)? 0 : -1;
+ result = !access (fname, R_OK)? 0 : -1;
xfree (fname);
- return i;
+ return result;
}
+/* Return the information about the secret key specified by the binary
+ keygrip GRIP. If the key is a shadowed one the shadow information
+ will be stored at the address R_SHADOW_INFO as an allocated
+ S-expression. */
+gpg_error_t
+agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
+ int *r_keytype, unsigned char **r_shadow_info)
+{
+ gpg_error_t err;
+ unsigned char *buf;
+ size_t len;
+ int keytype;
+
+ (void)ctrl;
+
+ if (r_keytype)
+ *r_keytype = PRIVATE_KEY_UNKNOWN;
+ if (r_shadow_info)
+ *r_shadow_info = NULL;
+
+ {
+ gcry_sexp_t sexp;
+
+ err = read_key_file (grip, &sexp);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_ENOENT)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ else
+ return err;
+ }
+ err = make_canon_sexp (sexp, &buf, &len);
+ gcry_sexp_release (sexp);
+ if (err)
+ return err;
+ }
+
+ keytype = agent_private_key_type (buf);
+ switch (keytype)
+ {
+ case PRIVATE_KEY_CLEAR:
+ break;
+ case PRIVATE_KEY_PROTECTED:
+ /* If we ever require it we could retrieve the comment fields
+ from such a key. */
+ break;
+ case PRIVATE_KEY_SHADOWED:
+ if (r_shadow_info)
+ {
+ const unsigned char *s;
+ size_t n;
+
+ err = agent_get_shadow_info (buf, &s);
+ if (!err)
+ {
+ n = gcry_sexp_canon_len (s, 0, NULL, NULL);
+ assert (n);
+ *r_shadow_info = xtrymalloc (n);
+ if (!*r_shadow_info)
+ err = gpg_error_from_syserror ();
+ else
+ memcpy (*r_shadow_info, s, n);
+ }
+ }
+ break;
+ default:
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ break;
+ }
+
+ if (!err && r_keytype)
+ *r_keytype = keytype;
+
+ xfree (buf);
+ return err;
+}
diff --git a/agent/protect.c b/agent/protect.c
index ebb02ac89..8b022ecfb 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1,6 +1,6 @@
/* protect.c - Un/Protect a secret key
* Copyright (C) 1998, 1999, 2000, 2001, 2002,
- * 2003, 2007 Free Software Foundation, Inc.
+ * 2003, 2007, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -1105,3 +1105,68 @@ agent_get_shadow_info (const unsigned char *shadowkey,
return 0;
}
+
+/* Parse the canonical encoded SHADOW_INFO S-expression. On success
+ the hex encoded serial number is returned as a malloced strings at
+ R_HEXSN and the Id string as a malloced string at R_IDSTR. On
+ error an error code is returned and NULL is stored at the result
+ parameters addresses. If the serial number or the ID string is not
+ required, NULL may be passed for them. */
+gpg_error_t
+parse_shadow_info (const unsigned char *shadow_info,
+ char **r_hexsn, char **r_idstr)
+{
+ const unsigned char *s;
+ size_t n;
+
+ if (r_hexsn)
+ *r_hexsn = NULL;
+ if (r_idstr)
+ *r_idstr = NULL;
+
+ s = shadow_info;
+ if (*s != '(')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++;
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+
+ if (r_hexsn)
+ {
+ *r_hexsn = bin2hex (s, n, NULL);
+ if (!*r_hexsn)
+ return gpg_error_from_syserror ();
+ }
+ s += n;
+
+ n = snext (&s);
+ if (!n)
+ {
+ if (r_hexsn)
+ {
+ xfree (*r_hexsn);
+ *r_hexsn = NULL;
+ }
+ return gpg_error (GPG_ERR_INV_SEXP);
+ }
+
+ if (r_idstr)
+ {
+ *r_idstr = xtrymalloc (n+1);
+ if (!*r_idstr)
+ {
+ if (r_hexsn)
+ {
+ xfree (*r_hexsn);
+ *r_hexsn = NULL;
+ }
+ return gpg_error_from_syserror ();
+ }
+ memcpy (*r_idstr, s, n);
+ (*r_idstr)[n] = 0;
+ }
+
+ return 0;
+}
+
diff --git a/common/ChangeLog b/common/ChangeLog
index 61ae679b4..f5ba7d725 100644
--- a/common/ChangeLog
+++ b/common/ChangeLog
@@ -1,3 +1,7 @@
+2009-03-06 Werner Koch <wk@g10code.com>
+
+ * sexputil.c (make_canon_sexp): New.
+
2009-03-03 Werner Koch <wk@g10code.com>
* exechelp.c (do_exec): Make sure that /dev/null connected FDs are
diff --git a/common/sexputil.c b/common/sexputil.c
index 4907a9355..4ff7b4955 100644
--- a/common/sexputil.c
+++ b/common/sexputil.c
@@ -1,5 +1,5 @@
/* sexputil.c - Utility functions for S-expressions.
- * Copyright (C) 2005, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -34,6 +34,42 @@
#include "util.h"
#include "sexp-parse.h"
+
+/* Helper function to create a a canonical encoded S-expression from a
+ Libgcrypt S-expression object. The function returns 0 on success
+ and the malloced canonical S-expression is stored at R_BUFFER and
+ the allocated length at R_BUFLEN. On error an error code is
+ returned and (NULL, 0) stored at R_BUFFER and R_BUFLEN. If the
+ allocated buffer length is not required, NULL by be used for
+ R_BUFLEN. */
+gpg_error_t
+make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen)
+{
+ size_t len;
+ unsigned char *buf;
+
+ *r_buffer = NULL;
+ if (r_buflen)
+ *r_buflen = 0;;
+
+ len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
+ if (!len)
+ return gpg_error (GPG_ERR_BUG);
+ buf = xtrymalloc (len);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len);
+ if (!len)
+ return gpg_error (GPG_ERR_BUG);
+
+ *r_buffer = buf;
+ if (r_buflen)
+ *r_buflen = len;
+
+ return 0;
+}
+
+
/* Return the so called "keygrip" which is the SHA-1 hash of the
public key parameters expressed in a way depended on the algorithm.
diff --git a/common/util.h b/common/util.h
index 66569e27e..d117f86bf 100644
--- a/common/util.h
+++ b/common/util.h
@@ -1,5 +1,5 @@
/* util.h - Utility functions for GnuPG
- * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -183,6 +183,8 @@ gpg_error_t b64dec_finish (struct b64state *state);
/*-- sexputil.c */
+gpg_error_t make_canon_sexp (gcry_sexp_t sexp,
+ unsigned char **r_buffer, size_t *r_buflen);
gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
unsigned char *grip);
int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi
index b4c92bbfa..659d546f2 100644
--- a/doc/gpgsm.texi
+++ b/doc/gpgsm.texi
@@ -240,10 +240,12 @@ optional @var{pattern}. Those pattern consist of a list of user ids
(@pxref{how-to-specify-a-user-id}). When used along with the
@option{--armor} option a few informational lines are prepended before
each block. There is one limitation: As there is no commonly agreed
-upon way to pack more than one certificate into an ASN.1 structure, the
-binary export (i.e. without using @option{armor}) works only for the
-export of one certificate. Thus it is required to specify a
-@var{pattern} which yields exactly one certificate.
+upon way to pack more than one certificate into an ASN.1 structure,
+the binary export (i.e. without using @option{armor}) works only for
+the export of one certificate. Thus it is required to specify a
+@var{pattern} which yields exactly one certificate. Ephemeral
+certificate are only exported if all @var{pattern} are given as
+fingerprints or keygrips.
@item --export-secret-key-p12 @var{key-id}
@opindex export
@@ -601,7 +603,9 @@ forth to @var{epoch} which is the number of seconds elapsed since the year
@item --with-ephemeral-keys
@opindex with-ephemeral-keys
-Include ephemeral flagged keys in the output of key listings.
+Include ephemeral flagged keys in the output of key listings. Note
+that they are included anyway if the key specification for a listing
+is given as fingerprint or keygrip.
@item --debug-level @var{level}
@opindex debug-level
diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c
index 927399d48..08b59e649 100644
--- a/kbx/keybox-search.c
+++ b/kbx/keybox-search.c
@@ -457,7 +457,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
#ifdef KEYBOX_WITH_X509
/* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
- We don't have the keygrips as meta data, thus wen need to parse the
+ We don't have the keygrips as meta data, thus we need to parse the
certificate. Fixme: We might want to return proper error codes
instead of failing a search for invalid certificates etc. */
static int
diff --git a/scd/ChangeLog b/scd/ChangeLog
index b1ec7479e..efc888fd7 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,10 @@
+2009-03-06 Werner Koch <wk@g10code.com>
+
+ * app-nks.c (do_learn_status): Factor code out to..
+ (do_learn_status_core): .. new.
+ (do_readcert, do_sign, do_decipher): Switch to SigG if needed.
+ (verify_pin): Use DESC also for keypad based verify.
+
2009-03-05 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_a_chv): Remove special case for keypads.
diff --git a/scd/app-nks.c b/scd/app-nks.c
index 6aa205697..1a520bbf0 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -308,17 +308,20 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
-
-static gpg_error_t
-do_learn_status (app_t app, ctrl_t ctrl)
+static void
+do_learn_status_core (app_t app, ctrl_t ctrl, int is_sigg)
{
gpg_error_t err;
char ct_buf[100], id_buf[100];
int i;
+ const char *tag;
- err = switch_application (app, 0);
- if (err)
- return err;
+ if (is_sigg)
+ tag = "SIGG";
+ else if (app->app_local->nks_version < 3)
+ tag = "DF01";
+ else
+ tag = "NKS3";
/* Output information about all useful objects in the NKS application. */
for (i=0; filelist[i].fid; i++)
@@ -326,7 +329,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
if (filelist[i].nks_ver > app->app_local->nks_version)
continue;
- if (filelist[i].is_sigg)
+ if (!!filelist[i].is_sigg != !!is_sigg)
continue;
if (filelist[i].certtype)
@@ -342,8 +345,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
read that many bytes. */
snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
- app->app_local->nks_version < 3? "DF01":"NKS3",
- filelist[i].fid);
+ tag, filelist[i].fid);
send_status_info (ctrl, "CERTINFO",
ct_buf, strlen (ct_buf),
id_buf, strlen (id_buf),
@@ -361,8 +363,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
else
{
snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
- app->app_local->nks_version < 3? "DF01":"NKS3",
- filelist[i].fid);
+ tag, filelist[i].fid);
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
id_buf, strlen (id_buf),
@@ -371,58 +372,26 @@ do_learn_status (app_t app, ctrl_t ctrl)
}
}
- err = switch_application (app, 1);
- if (err)
- return 0; /* Silently ignore if we can't swicth to SigG. */
- for (i=0; filelist[i].fid; i++)
- {
- if (filelist[i].nks_ver > app->app_local->nks_version)
- continue;
+}
- if (!filelist[i].is_sigg)
- continue;
- if (filelist[i].certtype)
- {
- size_t len;
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl)
+{
+ gpg_error_t err;
- len = app_help_read_length_of_cert (app->slot,
- filelist[i].fid, NULL);
- if (len)
- {
- /* FIXME: We should store the length in the application's
- context so that a following readcert does only need to
- read that many bytes. */
- snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
- snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
- filelist[i].fid);
- send_status_info (ctrl, "CERTINFO",
- ct_buf, strlen (ct_buf),
- id_buf, strlen (id_buf),
- NULL, (size_t)0);
- }
- }
- else if (filelist[i].iskeypair)
- {
- char gripstr[40+1];
+ err = switch_application (app, 0);
+ if (err)
+ return err;
+
+ do_learn_status_core (app, ctrl, 0);
- err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
- if (err)
- log_error ("can't get keygrip from FID 0x%04X: %s\n",
- filelist[i].fid, gpg_strerror (err));
- else
- {
- snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
- filelist[i].fid);
- send_status_info (ctrl, "KEYPAIRINFO",
- gripstr, 40,
- id_buf, strlen (id_buf),
- NULL, (size_t)0);
- }
- }
- }
+ err = switch_application (app, 1);
+ if (err)
+ return 0; /* Silently ignore if we can't switch to SigG. */
+ do_learn_status_core (app, ctrl, 1);
return 0;
}
@@ -446,20 +415,24 @@ do_readcert (app_t app, const char *certid,
int class, tag, constructed, ndef;
size_t totobjlen, objlen, hdrlen;
int rootca = 0;
+ int is_sigg = 0;
*cert = NULL;
*certlen = 0;
- err = switch_application (app, 0);
- if (err)
- return err;
-
if (!strncmp (certid, "NKS-NKS3.", 9))
;
else if (!strncmp (certid, "NKS-DF01.", 9))
;
+ else if (!strncmp (certid, "NKS-SIGG.", 9))
+ is_sigg = 1;
else
return gpg_error (GPG_ERR_INV_ID);
+
+ err = switch_application (app, is_sigg);
+ if (err)
+ return err;
+
certid += 9;
if (!hexdigitp (certid) || !hexdigitp (certid+1)
|| !hexdigitp (certid+2) || !hexdigitp (certid+3)
@@ -603,9 +576,7 @@ verify_pin (app_t app, int pwid, const char *desc,
if (!opt.disable_keypad
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
{
- rc = pincb (pincb_arg,
- _("||Please enter your PIN at the reader's keypad"),
- NULL);
+ rc = pincb (pincb_arg, desc, NULL);
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
@@ -613,11 +584,8 @@ verify_pin (app_t app, int pwid, const char *desc,
return rc;
}
- /* Although it is possible to use a local PIN, we use the global
- PIN for this application. */
- rc = iso7816_verify_kp (app->slot, 0, "", 0, &pininfo);
- /* Dismiss the prompt. */
- pincb (pincb_arg, NULL, NULL);
+ rc = iso7816_verify_kp (app->slot, pwid, "", 0, &pininfo);
+ pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
}
else
{
@@ -630,8 +598,6 @@ verify_pin (app_t app, int pwid, const char *desc,
return rc;
}
- /* The following limits are due to TCOS but also defined in the
- NKS specs. */
rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen);
if (rc)
{
@@ -675,6 +641,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
int rc, i;
+ int is_sigg = 0;
int fid;
unsigned char data[35]; /* Must be large enough for a SHA-1 digest
+ the largest OID _prefix above. */
@@ -684,19 +651,22 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
if (indatalen != 20 && indatalen != 16 && indatalen != 35)
return gpg_error (GPG_ERR_INV_VALUE);
- rc = switch_application (app, 0);
- if (rc)
- return rc;
-
/* Check that the provided ID is valid. This is not really needed
but we do it to enforce correct usage by the caller. */
if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
;
else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
;
+ else if (!strncmp (keyidstr, "NKS-SIGG.", 9) )
+ is_sigg = 1;
else
return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9;
+
+ rc = switch_application (app, is_sigg);
+ if (rc)
+ return rc;
+
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
|| !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
|| keyidstr[4])
@@ -743,7 +713,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
-
/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
If a PIN is required the PINCB will be used to ask for the PIN; it
should return the PIN in an allocated buffer and put it into PIN. */
@@ -759,24 +728,28 @@ do_decipher (app_t app, const char *keyidstr,
0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
};
int rc, i;
+ int is_sigg = 0;
int fid;
if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE);
- rc = switch_application (app, 0);
- if (rc)
- return rc;
-
/* Check that the provided ID is valid. This is not really needed
but we do it to to enforce correct usage by the caller. */
if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
;
else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
;
+ else if (!strncmp (keyidstr, "NKS-SIGG.", 9) )
+ is_sigg = 1;
else
return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9;
+
+ rc = switch_application (app, is_sigg);
+ if (rc)
+ return rc;
+
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
|| !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
|| keyidstr[4])
diff --git a/sm/ChangeLog b/sm/ChangeLog
index 83f4569ca..4d98dc50e 100644
--- a/sm/ChangeLog
+++ b/sm/ChangeLog
@@ -1,3 +1,14 @@
+2009-03-06 Werner Koch <wk@g10code.com>
+
+ * call-agent.c (gpgsm_agent_keyinfo, keyinfo_status_cb): New.
+ * keylist.c (list_cert_colon): Print card S/N.
+
+ * keylist.c (list_internal_keys): Always list ephemeral keys if
+ specified by keygrip or fingerprint.
+ (list_cert_raw): Always show ephemeral flag.
+ * export.c (gpgsm_export): Export ephemeral keys if specified by
+ keygrip.
+
2009-02-09 Werner Koch <wk@g10code.com>
* gpgsm.c (main): Change default cipher back to 3DES.
@@ -2451,7 +2462,7 @@ h2007-11-22 Werner Koch <wk@g10code.com>
Copyright 2001, 2002, 2003, 2004, 2005, 2006,
- 2007, 2008 Free Software Foundation, Inc.
+ 2007, 2008, 2009 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
diff --git a/sm/call-agent.c b/sm/call-agent.c
index 0add44aae..a6ac6da36 100644
--- a/sm/call-agent.c
+++ b/sm/call-agent.c
@@ -1,6 +1,6 @@
/* call-agent.c - Divert GPGSM operations to the agent
* Copyright (C) 2001, 2002, 2003, 2005, 2007,
- * 2008 Free Software Foundation, Inc.
+ * 2008, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -836,3 +836,68 @@ gpgsm_agent_send_nop (ctrl_t ctrl)
}
+
+static int
+keyinfo_status_cb (void *opaque, const char *line)
+{
+ char **serialno = opaque;
+ const char *s, *s2;
+
+ if (!strncmp (line, "KEYINFO ", 8) && !*serialno)
+ {
+ s = strchr (line+8, ' ');
+ if (s && s[1] == 'T' && s[2] == ' ' && s[3])
+ {
+ s += 3;
+ s2 = strchr (s, ' ');
+ if ( s2 > s )
+ {
+ *serialno = xtrymalloc ((s2 - s)+1);
+ if (*serialno)
+ {
+ memcpy (*serialno, s, s2 - s);
+ (*serialno)[s2 - s] = 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Return the serial number for a secret key. If the returned serial
+ number is NULL, the key is not stored on a smartcard. Caller needs
+ to free R_SERIALNO. */
+gpg_error_t
+gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
+{
+ gpg_error_t err;
+ char line[ASSUAN_LINELENGTH];
+ char *serialno = NULL;
+
+ *r_serialno = NULL;
+
+ err = start_agent (ctrl);
+ if (err)
+ return err;
+
+ 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;
+
+ err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
+ keyinfo_status_cb, &serialno);
+ if (!err && serialno)
+ {
+ /* Sanity check for bad characters. */
+ if (strpbrk (serialno, ":\n\r"))
+ err = GPG_ERR_INV_VALUE;
+ }
+ if (err)
+ xfree (serialno);
+ else
+ *r_serialno = serialno;
+ return err;
+}
+
diff --git a/sm/export.c b/sm/export.c
index fa2e9de1a..f8e23cec1 100644
--- a/sm/export.c
+++ b/sm/export.c
@@ -1,5 +1,5 @@
/* export.c - Export certificates and private keys.
- * Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003, 2004, 2007, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -37,11 +37,11 @@
/* A table to store a fingerprint as used in a duplicates table. We
- don't need to hash here because a fingerprint is alrady a perfect
+ don't need to hash here because a fingerprint is already a perfect
hash value. This we use the most significant bits to index the
table and then use a linked list for the overflow. Possible
- enhancement for very large number of certictates: Add a second
- level table and then resort to a linked list. */
+ enhancement for very large number of certificates: Add a second
+ level table and then resort to a linked list. */
struct duptable_s
{
struct duptable_s *next;
@@ -192,18 +192,16 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream)
}
}
- /* If all specifications are done by fingerprint, we switch to
- ephemeral mode so that _all_ currently available and matching
- certificates are exported.
-
- fixme: we should in this case keep a list of certificates to
- avoid accidential export of duplicate certificates. */
+ /* If all specifications are done by fingerprint or keygrip, we
+ switch to ephemeral mode so that _all_ currently available and
+ matching certificates are exported. */
if (names && ndesc)
{
for (i=0; (i < ndesc
&& (desc[i].mode == KEYDB_SEARCH_MODE_FPR
|| desc[i].mode == KEYDB_SEARCH_MODE_FPR20
- || desc[i].mode == KEYDB_SEARCH_MODE_FPR16)); i++)
+ || desc[i].mode == KEYDB_SEARCH_MODE_FPR16
+ || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++)
;
if (i == ndesc)
keydb_set_ephemeral (hd, 1);
@@ -228,7 +226,7 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream)
rc = insert_duptable (dtable, fpr, &exists);
if (rc)
{
- log_error ("inserting into duplicates table fauiled: %s\n",
+ log_error ("inserting into duplicates table failed: %s\n",
gpg_strerror (rc));
goto leave;
}
diff --git a/sm/fingerprint.c b/sm/fingerprint.c
index 6581688a8..addf56296 100644
--- a/sm/fingerprint.c
+++ b/sm/fingerprint.c
@@ -196,8 +196,8 @@ gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array)
return array;
}
-/* Return an allocated buffer with the keygrip of CERT in from of an
- hexstring. NULL is returned in case of error */
+/* Return an allocated buffer with the keygrip of CERT encoded as a
+ hexstring. NULL is returned in case of error. */
char *
gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
{
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index 1460dc0d6..984dda135 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -395,6 +395,8 @@ int gpgsm_agent_learn (ctrl_t ctrl);
int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);
+gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
+ char **r_serialno);
/*-- call-dirmngr.c --*/
int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
diff --git a/sm/keylist.c b/sm/keylist.c
index b0d2f56dd..39b82c29d 100644
--- a/sm/keylist.c
+++ b/sm/keylist.c
@@ -1,6 +1,6 @@
/* keylist.c - Print certificates in various formats.
* Copyright (C) 1998, 1999, 2000, 2001, 2003,
- * 2004, 2005, 2008 Free Software Foundation, Inc.
+ * 2004, 2005, 2008, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -481,7 +481,24 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
es_putc (':', fp);
/* Field 12, capabilities: */
print_capabilities (cert, fp);
+ /* Field 13, not used: */
es_putc (':', fp);
+ if (have_secret)
+ {
+ char *cardsn;
+
+ p = gpgsm_get_keygrip_hexstring (cert);
+ if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn)
+ {
+ /* Field 14, not used: */
+ es_putc (':', fp);
+ /* Field 15: Token serial number. */
+ es_fputs (cardsn, fp);
+ es_putc (':', fp);
+ }
+ xfree (cardsn);
+ xfree (p);
+ }
es_putc ('\n', fp);
/* FPR record */
@@ -989,7 +1006,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err));
}
- if (opt.with_ephemeral_keys && hd)
+ if (hd)
{
unsigned int blobflags;
@@ -1275,6 +1292,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
gpg_error_t rc = 0;
const char *lastresname, *resname;
int have_secret;
+ int want_ephemeral = opt.with_ephemeral_keys;
hd = keydb_new (0);
if (!hd)
@@ -1319,7 +1337,24 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
}
- if (opt.with_ephemeral_keys)
+ /* If all specifications are done by fingerprint or keygrip, we
+ switch to ephemeral mode so that _all_ currently available and
+ matching certificates are listed. */
+ if (!want_ephemeral && names && ndesc)
+ {
+ int i;
+
+ for (i=0; (i < ndesc
+ && (desc[i].mode == KEYDB_SEARCH_MODE_FPR
+ || desc[i].mode == KEYDB_SEARCH_MODE_FPR20
+ || desc[i].mode == KEYDB_SEARCH_MODE_FPR16
+ || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++)
+ ;
+ if (i == ndesc)
+ want_ephemeral = 1;
+ }
+
+ if (want_ephemeral)
keydb_set_ephemeral (hd, 1);
/* It would be nice to see which of the given users did actually