diff --git a/tools/Makefile.am b/tools/Makefile.am index e29e6a281..f74221b2d 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -123,7 +123,13 @@ gpg_connect_agent_LDADD = ../common/libgpgrl.a $(common_libs) \ $(LIBREADLINE) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ $(gpg_connect_agent_rc_objs) -gpg_card_tool_SOURCES = gpg-card-tool.c card-tool.h card-call-scd.c + +gpg_card_tool_SOURCES = \ + gpg-card-tool.c \ + card-tool.h \ + card-call-scd.c \ + card-tool-misc.c + gpg_card_tool_LDADD = ../common/libgpgrl.a $(common_libs) \ $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \ $(GPG_ERROR_LIBS) \ diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c index 7df7861d3..abf35ed17 100644 --- a/tools/card-call-scd.c +++ b/tools/card-call-scd.c @@ -132,6 +132,7 @@ release_card_info (card_info_t info) { int i; + if (!info) return; @@ -145,12 +146,18 @@ release_card_info (card_info_t info) xfree (info->pubkey_url); info->pubkey_url = NULL; xfree (info->login_data); info->login_data = NULL; info->cafpr1len = info->cafpr2len = info->cafpr3len = 0; - info->fpr1len = info->fpr2len = info->fpr3len = 0; for (i=0; i < DIM(info->private_do); i++) { xfree (info->private_do[i]); info->private_do[i] = NULL; } + while (info->kinfo) + { + key_info_t kinfo = info->kinfo->next; + xfree (info->kinfo); + info->kinfo = kinfo; + } + } @@ -534,6 +541,48 @@ get_serialno_cb (void *opaque, const char *line) } + +/* For historical reasons OpenPGP cards simply use the numbers 1 to 3 + * for the . Other cards and future versions of + * scd/app-openpgp.c may print the full keyref; i.e. "OpenPGP.1" + * instead of "1". This is a helper to cope with that. */ +static const char * +parse_keyref_helper (const char *string) +{ + if (*string == '1' && spacep (string+1)) + return "OPENPGP.1"; + else if (*string == '2' && spacep (string+1)) + return "OPENPGP.2"; + else if (*string == '3' && spacep (string+1)) + return "OPENPGP.3"; + else + return string; +} + + +/* Create a new key info object with KEYREF. All fields but the + * keyref are zeroed out. Never returns NULL. The created object is + * appended to the list at INFO. */ +static key_info_t +create_kinfo (card_info_t info, const char *keyref) +{ + key_info_t kinfo, ki; + + kinfo = xcalloc (1, sizeof *kinfo + strlen (keyref)); + strcpy (kinfo->keyref, keyref); + + if (!info->kinfo) + info->kinfo = kinfo; + else + { + for (ki=info->kinfo; ki->next; ki = ki->next) + ; + ki->next = kinfo; + } + return kinfo; +} + + /* The status callback to handle the LEARN and GETATTR commands. */ static gpg_error_t learn_status_cb (void *opaque, const char *line) @@ -541,6 +590,10 @@ learn_status_cb (void *opaque, const char *line) struct card_info_s *parm = opaque; const char *keyword = line; int keywordlen; + char *line_buffer = NULL; /* In case we need a copy. */ + char *pline; + key_info_t kinfo; + const char *keyref; int i; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) @@ -635,18 +688,31 @@ learn_status_cb (void *opaque, const char *line) } else if (!memcmp (keyword, "KEY-FPR", keywordlen)) { - int no = atoi (line); + /* The format of such a line is: + * KEY-FPR + */ + const char *fpr; - while (*line && !spacep (line)) - line++; - while (spacep (line)) - line++; - if (no == 1) - parm->fpr1len = unhexify_fpr (line, parm->fpr1, sizeof parm->fpr1); - else if (no == 2) - parm->fpr2len = unhexify_fpr (line, parm->fpr2, sizeof parm->fpr2); - else if (no == 3) - parm->fpr3len = unhexify_fpr (line, parm->fpr3, sizeof parm->fpr3); + line_buffer = pline = xstrdup (line); + + keyref = parse_keyref_helper (pline); + while (*pline && !spacep (pline)) + pline++; + if (*pline) + *pline++ = 0; /* Terminate keyref. */ + while (spacep (pline)) /* Skip to the fingerprint. */ + pline++; + fpr = pline; + + /* Check whether we already have an item for the keyref. */ + kinfo = find_kinfo (parm, keyref); + if (!kinfo) /* No: new entry. */ + kinfo = create_kinfo (parm, keyref); + else /* Existing entry - clear the fpr. */ + memset (kinfo->fpr, 0, sizeof kinfo->fpr); + + /* Set or update or the fingerprint. */ + kinfo->fprlen = unhexify_fpr (fpr, kinfo->fpr, sizeof kinfo->fpr); } break; @@ -664,17 +730,28 @@ learn_status_cb (void *opaque, const char *line) } else if (!memcmp (keyword, "KEY-TIME", keywordlen)) { - int no = atoi (line); - while (* line && !spacep (line)) - line++; - while (spacep (line)) - line++; - if (no == 1) - parm->fpr1time = strtoul (line, NULL, 10); - else if (no == 2) - parm->fpr2time = strtoul (line, NULL, 10); - else if (no == 3) - parm->fpr3time = strtoul (line, NULL, 10); + /* The format of such a line is: + * KEY-TIME + */ + const char *timestamp; + + line_buffer = pline = xstrdup (line); + + keyref = parse_keyref_helper (pline); + while (*pline && !spacep (pline)) + pline++; + if (*pline) + *pline++ = 0; /* Terminate keyref. */ + while (spacep (pline)) /* Skip to the timestamp. */ + pline++; + timestamp = pline; + + /* Check whether we already have an item for the keyref. */ + kinfo = find_kinfo (parm, keyref); + if (!kinfo) /* No: new entry. */ + kinfo = create_kinfo (parm, keyref); + + kinfo->created = strtoul (timestamp, NULL, 10); } else if (!memcmp (keyword, "KEY-ATTR", keywordlen)) { @@ -767,21 +844,29 @@ learn_status_cb (void *opaque, const char *line) } else if (!memcmp (keyword, "KEYPAIRINFO", keywordlen)) { + /* The format of such a line is: + * KEYPARINFO + */ const char *hexgrp = line; - int no; while (*line && !spacep (line)) line++; while (spacep (line)) line++; - if (strncmp (line, "OPENPGP.", 8)) - ; - else if ((no = atoi (line+8)) == 1) - unhexify_fpr (hexgrp, parm->grp1, sizeof parm->grp1); - else if (no == 2) - unhexify_fpr (hexgrp, parm->grp2, sizeof parm->grp2); - else if (no == 3) - unhexify_fpr (hexgrp, parm->grp3, sizeof parm->grp3); + + keyref = line; + + /* Check whether we already have an item for the keyref. */ + kinfo = find_kinfo (parm, keyref); + if (!kinfo) /* New entry. */ + kinfo = create_kinfo (parm, keyref); + else /* Existing entry - clear the grip. */ + memset (kinfo->grip, 0, sizeof kinfo->grip); + + /* Set or update the grip. Note that due to the + * calloc/memset an erroneous too short grip will be nul + * padded on the right. */ + unhexify_fpr (hexgrp, kinfo->grip, sizeof kinfo->grip); } break; @@ -809,6 +894,7 @@ learn_status_cb (void *opaque, const char *line) break; } + xfree (line_buffer); return 0; } diff --git a/tools/card-tool-misc.c b/tools/card-tool-misc.c new file mode 100644 index 000000000..0f5fcc0a0 --- /dev/null +++ b/tools/card-tool-misc.c @@ -0,0 +1,44 @@ +/* card-tool-misc.c - Helper functions for gpg-card-tool + * Copyright (C) 2019 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +#include "../common/util.h" +#include "../common/i18n.h" +#include "../common/openpgpdefs.h" +#include "card-tool.h" + +/* Return the key info object for the key KEYREF. If it is not found + * NULL is returned. */ +key_info_t +find_kinfo (card_info_t info, const char *keyref) +{ + key_info_t kinfo; + + for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next) + if (!strcmp (kinfo->keyref, keyref)) + return kinfo; + return NULL; +} diff --git a/tools/card-tool.h b/tools/card-tool.h index d51698c76..0af618676 100644 --- a/tools/card-tool.h +++ b/tools/card-tool.h @@ -76,6 +76,26 @@ struct key_attr }; }; +/* An object to store information pertaining to a key pair. This is + * commonly used as a linked list with all keys known for the current + * card. */ +struct key_info_s +{ + struct key_info_s *next; + + unsigned char grip[20];/* The keygrip. */ + + unsigned char xflag; /* Temporary flag to help processing a list. */ + + /* The three next items are mostly useful for OpenPGP cards. */ + unsigned char fprlen; /* Use length of the next item. */ + unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN. */ + u32 created; /* The time the key was created. */ + + char keyref[1]; /* String with the keyref (e.g. OPENPGP.1). */ +}; +typedef struct key_info_s *key_info_t; + /* * The object used to store information about a card. @@ -100,18 +120,7 @@ struct card_info_s char cafpr1[20]; char cafpr2[20]; char cafpr3[20]; - unsigned char fpr1len; /* Length of the fingerprint or 0 if invalid. */ - unsigned char fpr2len; - unsigned char fpr3len; - char fpr1[20]; - char fpr2[20]; - char fpr3[20]; - u32 fpr1time; - u32 fpr2time; - u32 fpr3time; - char grp1[20]; /* The keygrip for OPENPGP.1 */ - char grp2[20]; /* The keygrip for OPENPGP.2 */ - char grp3[20]; /* The keygrip for OPENPGP.3 */ + key_info_t kinfo; /* Linked list with all keypair related data. */ unsigned long sig_counter; int chv1_cached; /* True if a PIN is not required for each signing. Note that the gpg-agent might cache @@ -133,6 +142,10 @@ struct card_info_s typedef struct card_info_s *card_info_t; +/*-- card-tool-misc.c --*/ +key_info_t find_kinfo (card_info_t info, const char *keyref); + + /*-- card-call-scd.c --*/ void release_card_info (card_info_t info); const char *app_type_string (app_type_t app_type); diff --git a/tools/gpg-card-tool.c b/tools/gpg-card-tool.c index b40914a6f..31d9c220e 100644 --- a/tools/gpg-card-tool.c +++ b/tools/gpg-card-tool.c @@ -107,10 +107,19 @@ static struct debug_flags_s debug_flags [] = }; +/* An object to create lists of labels and keyrefs. */ +struct keyinfolabel_s +{ + const char *label; + const char *keyref; +}; +typedef struct keyinfolabel_s *keyinfolabel_t; + + /* Limit of size of data we read from a file for certain commands. */ #define MAX_GET_DATA_FROM_FILE 16384 -/* Constats for OpenPGP cards. */ +/* Constants for OpenPGP cards. */ #define OPENPGP_USER_PIN_DEFAULT "123456" #define OPENPGP_ADMIN_PIN_DEFAULT "12345678" #define OPENPGP_KDF_DATA_LENGTH_MIN 90 @@ -544,35 +553,101 @@ print_isoname (estream_t fp, const char *name) } -/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */ +/* Return true if the buffer MEM of length memlen consists only of zeroes. */ static int -fpr_is_zero (const char *fpr, unsigned int fprlen) +mem_is_zero (const char *mem, unsigned int memlen) { int i; - for (i=0; i < fprlen && !fpr[i]; i++) + for (i=0; i < memlen && !mem[i]; i++) ; - return (i == fprlen); + return (i == memlen); } -/* Return true if the fingerprint FPR consists only of 0xFF. */ +/* Return true if the buffer MEM or length MEMLEN consists only of 0xFF. */ static int -fpr_is_ff (const char *fpr, unsigned int fprlen) +mem_is_ff (const char *mem, unsigned int memlen) { int i; - for (i=0; i < fprlen && fpr[i] == '\xff'; i++) + for (i=0; i < memlen && mem[i] == '\xff'; i++) ; - return (i == fprlen); + return (i == memlen); } +/* Helper to list a single keyref. */ +static void +list_one_kinfo (key_info_t kinfo, estream_t fp) +{ + if (kinfo) + { + tty_fprintf (fp, " "); + if (mem_is_zero (kinfo->grip, sizeof kinfo->grip)) + tty_fprintf (fp, "[none]\n"); + else + print_keygrip (fp, kinfo->grip); + + if (kinfo->fprlen && kinfo->created) + { + tty_fprintf (fp, " fingerprint :"); + print_shax_fpr (fp, kinfo->fpr, kinfo->fprlen); + tty_fprintf (fp, " created ....: %s\n", + isotimestamp (kinfo->created)); + } + } + else + tty_fprintf (fp, " [none]\n"); +} + + +/* List all keyinfo in INFO using the list of LABELS. */ +static void +list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp) +{ + key_info_t kinfo; + int idx, i; + + /* Print the keyinfo. We first print those we known and then all + * remaining item. */ + for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next) + kinfo->xflag = 0; + if (labels) + { + for (idx=0; labels[idx].label; idx++) + { + tty_fprintf (fp, "%s", labels[idx].label); + kinfo = find_kinfo (info, labels[idx].keyref); + list_one_kinfo (kinfo, fp); + if (kinfo) + kinfo->xflag = 1; + } + } + for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next) + { + if (kinfo->xflag) + continue; + tty_fprintf (fp, "Key %s ", kinfo->keyref); + for (i=5+strlen (kinfo->keyref); i < 18; i++) + tty_fprintf (fp, "."); + tty_fprintf (fp, ":"); + list_one_kinfo (kinfo, fp); + } +} + + /* List OpenPGP card specific data. */ static void list_openpgp (card_info_t info, estream_t fp) { + static struct keyinfolabel_s keyinfolabels[] = { + { "Signature key ....:", "OPENPGP.1" }, + { "Encryption key....:", "OPENPGP.2" }, + { "Authentication key:", "OPENPGP.3" }, + { NULL, NULL } + }; int i; if (!info->serialno @@ -661,33 +736,8 @@ list_openpgp (card_info_t info, estream_t fp) info->uif[0] ? "on" : "off", info->uif[1] ? "on" : "off", info->uif[2] ? "on" : "off"); } - tty_fprintf (fp, "Signature key ....:"); - print_shax_fpr (fp, info->fpr1len? info->fpr1:NULL, info->fpr1len); - if (info->fpr1len && info->fpr1time) - { - tty_fprintf (fp, " created ....: %s\n", - isotimestamp (info->fpr1time)); - tty_fprintf (fp, " keygrip ....: "); - print_keygrip (fp, info->grp1); - } - tty_fprintf (fp, "Encryption key....:"); - print_shax_fpr (fp, info->fpr2len? info->fpr2:NULL, info->fpr2len); - if (info->fpr2len && info->fpr2time) - { - tty_fprintf (fp, " created ....: %s\n", - isotimestamp (info->fpr2time)); - tty_fprintf (fp, " keygrip ....: "); - print_keygrip (fp, info->grp2); - } - tty_fprintf (fp, "Authentication key:"); - print_shax_fpr (fp, info->fpr3len? info->fpr3:NULL, info->fpr3len); - if (info->fpr3len && info->fpr3time) - { - tty_fprintf (fp, " created ....: %s\n", - isotimestamp (info->fpr3time)); - tty_fprintf (fp, " keygrip ....: "); - print_keygrip (fp, info->grp3); - } + + list_all_kinfo (info, keyinfolabels, fp); /* tty_fprintf (fp, "General key info->.: "); */ /* thefpr = (info->fpr1len? info->fpr1 : info->fpr2len? info->fpr2 : */ @@ -696,7 +746,7 @@ list_openpgp (card_info_t info, estream_t fp) /* info->fpr3len? info->fpr3len : 0); */ /* If the fingerprint is all 0xff, the key has no associated OpenPGP certificate. */ - /* if ( thefpr && !fpr_is_ff (thefpr, thefprlen) */ + /* if ( thefpr && !mem_is_ff (thefpr, thefprlen) */ /* && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, thefprlen)) */ /* { */ /* print_pubkey_info (ctrl, fp, pk); */ @@ -900,6 +950,7 @@ static gpg_error_t cmd_fetch (card_info_t info) { gpg_error_t err; + key_info_t kinfo; if (!info) return print_help @@ -916,7 +967,7 @@ cmd_fetch (card_info_t info) /* free_strlist (sl); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ } - else if (info->fpr1len) + else if ((kinfo = find_kinfo (info, "OPENPGP.1")) && kinfo->fprlen) { /* rc = keyserver_import_fprint (ctrl, info.fpr1, info.fpr1len, */ /* opt.keyserver, 0); */ @@ -1479,6 +1530,7 @@ cmd_generate (card_info_t info) int forced_chv1 = -1; int want_backup; char *answer = NULL; + key_info_t kinfo1, kinfo2, kinfo3; if (!info) return print_help @@ -1507,9 +1559,15 @@ cmd_generate (card_info_t info) else want_backup = 0; - if ( (info->fpr1len && !fpr_is_zero (info->fpr1, info->fpr1len)) - || (info->fpr2len && !fpr_is_zero (info->fpr2, info->fpr2len)) - || (info->fpr3len && !fpr_is_zero (info->fpr3, info->fpr3len))) + + kinfo1 = find_kinfo (info, "OPENPGP.1"); + kinfo2 = find_kinfo (info, "OPENPGP.2"); + kinfo3 = find_kinfo (info, "OPENPGP.3"); + + if ((kinfo1 && kinfo1->fprlen && !mem_is_zero (kinfo1->fpr,kinfo1->fprlen)) + || (kinfo2 && kinfo2->fprlen && !mem_is_zero (kinfo2->fpr,kinfo2->fprlen)) + || (kinfo3 && kinfo3->fprlen && !mem_is_zero (kinfo3->fpr,kinfo3->fprlen)) + ) { tty_printf ("\n"); log_info (_("Note: keys are already stored on the card!\n"));