aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2020-05-28 11:00:27 +0000
committerWerner Koch <[email protected]>2020-05-28 11:00:27 +0000
commited0759f39be04dd6108237f5ed03c7cfd1cb4642 (patch)
tree3d1d3e6c165310d26215332f167d73e76313ccf5
parentcard: Update card info after "generate". (diff)
downloadgnupg-ed0759f39be04dd6108237f5ed03c7cfd1cb4642.tar.gz
gnupg-ed0759f39be04dd6108237f5ed03c7cfd1cb4642.zip
card: New command "apdu"
* tools/card-call-scd.c (scd_apdu): Add optional arg 'options'. * tools/gpg-card.c (cmd_apdu): New. (enum cmdids): Add cmdAPDU. (dispatch_command): Add command "apdu". (interactive_loop): Ditto. -- This command is hidden because it can be used to brick a card. The command is basically the same as sending "scd apdu" in gpg-connect-agent but here we do full decoding and printing in hex. Signed-off-by: Werner Koch <[email protected]>
-rw-r--r--tools/card-call-scd.c10
-rw-r--r--tools/card-misc.c2
-rw-r--r--tools/gpg-card.c102
-rw-r--r--tools/gpg-card.h3
4 files changed, 108 insertions, 9 deletions
diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c
index 340646362..1116e072e 100644
--- a/tools/card-call-scd.c
+++ b/tools/card-call-scd.c
@@ -450,10 +450,11 @@ store_serialno (const char *line)
* stored at R_SW inless R_SW is NULL. With HEXAPDU being NULL only a
* RESET command is send to scd. With HEXAPDU being the string
* "undefined" the command "SERIALNO undefined" is send to scd. If
- * R_DATA is not NULL the data is without the status code is stored
- * there. Caller must release it. */
+ * R_DATA is not NULL the data without the status code is stored
+ * there. Caller must release it. If OPTIONS is not NULL, this will
+ * be passed verbatim to the SCDaemon's APDU command. */
gpg_error_t
-scd_apdu (const char *hexapdu, unsigned int *r_sw,
+scd_apdu (const char *hexapdu, const char *options, unsigned int *r_sw,
unsigned char **r_data, size_t *r_datalen)
{
gpg_error_t err;
@@ -487,7 +488,8 @@ scd_apdu (const char *hexapdu, unsigned int *r_sw,
init_membuf (&mb, 256);
- snprintf (line, DIM(line), "SCD APDU %s", hexapdu);
+ snprintf (line, DIM(line), "SCD APDU %s%s%s",
+ options?options:"", options?" -- ":"", hexapdu);
err = assuan_transact (agent_ctx, line,
put_membuf_cb, &mb, NULL, NULL, NULL, NULL);
if (!err)
diff --git a/tools/card-misc.c b/tools/card-misc.c
index bccdbda9d..bbb2b4810 100644
--- a/tools/card-misc.c
+++ b/tools/card-misc.c
@@ -88,7 +88,7 @@ send_apdu (const char *hexapdu, const char *desc, unsigned int ignore,
gpg_error_t err;
unsigned int sw;
- err = scd_apdu (hexapdu, &sw, r_data, r_datalen);
+ err = scd_apdu (hexapdu, NULL, &sw, r_data, r_datalen);
if (err)
log_error ("sending card command %s failed: %s\n", desc,
gpg_strerror (err));
diff --git a/tools/gpg-card.c b/tools/gpg-card.c
index 0496ea058..1facb5302 100644
--- a/tools/gpg-card.c
+++ b/tools/gpg-card.c
@@ -3137,6 +3137,99 @@ cmd_yubikey (card_info_t info, char *argstr)
return err;
}
+
+static gpg_error_t
+cmd_apdu (card_info_t info, char *argstr)
+{
+ gpg_error_t err;
+ estream_t fp = opt.interactive? NULL : es_stdout;
+ int with_atr;
+ int handle_more;
+ const char *s;
+ const char *exlenstr;
+ int exlenstrlen;
+ char *options = NULL;
+ unsigned int sw;
+ unsigned char *result = NULL;
+ size_t i, j, resultlen;
+
+ if (!info)
+ return print_help
+ ("APDU [--more] [--exlen[=N]] <hexstring>\n"
+ "\n"
+ "Send an APDU to the current card. This command bypasses the high\n"
+ "level functions and sends the data directly to the card. HEXSTRING\n"
+ "is expected to be a proper APDU.\n"
+ "\n"
+ "Using the option \"--more\" handles the card status word MORE_DATA\n"
+ "(61xx) and concatenates all responses to one block.\n"
+ "\n"
+ "Using the option \"--exlen\" the returned APDU may use extended\n"
+ "length up to N bytes. If N is not given a default value is used.\n",
+ 0);
+
+ if (has_option (argstr, "--dump-atr"))
+ with_atr = 2;
+ else
+ with_atr = has_option (argstr, "--atr");
+ handle_more = has_option (argstr, "--more");
+
+ exlenstr = has_option_name (argstr, "--exlen");
+ exlenstrlen = 0;
+ if (exlenstr)
+ {
+ for (s=exlenstr; *s && !spacep (s); s++)
+ exlenstrlen++;
+ }
+
+ argstr = skip_options (argstr);
+
+ if (with_atr || handle_more || exlenstr)
+ options = xasprintf ("%s%s%s%.*s",
+ with_atr == 2? " --dump-atr": with_atr? " --atr":"",
+ handle_more?" --more":"",
+ exlenstr?" ":"", exlenstrlen, exlenstr?exlenstr:"");
+
+ err = scd_apdu (argstr, options, &sw, &result, &resultlen);
+ if (err)
+ goto leave;
+ log_info ("Statusword: 0x%04x\n", sw);
+ for (i=0; i < resultlen; )
+ {
+ size_t save_i = i;
+
+ tty_fprintf (fp, "D[%04X] ", (unsigned int)i);
+ for (j=0; j < 16 ; j++, i++)
+ {
+ if (j == 8)
+ tty_fprintf (fp, " ");
+ if (i < resultlen)
+ tty_fprintf (fp, " %02X", result[i]);
+ else
+ tty_fprintf (fp, " ");
+ }
+ tty_fprintf (fp, " ");
+ i = save_i;
+ for (j=0; j < 16; j++, i++)
+ {
+ unsigned int c = result[i];
+ if ( i >= resultlen )
+ tty_fprintf (fp, " ");
+ else if (isascii (c) && isprint (c) && !iscntrl (c))
+ tty_fprintf (fp, "%c", c);
+ else
+ tty_fprintf (fp, ".");
+ }
+ tty_fprintf (fp, "\n");
+ }
+
+ leave:
+ xfree (result);
+ xfree (options);
+ return err;
+}
+
+
/* Data used by the command parser. This needs to be outside of the
@@ -3148,7 +3241,7 @@ enum cmdids
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
- cmdUIF, cmdAUTH, cmdYUBIKEY,
+ cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU,
cmdINVCMD
};
@@ -3189,6 +3282,7 @@ static struct
{ "writecert", cmdWRITECERT, N_("store a certificate to a data object")},
{ "writekey", cmdWRITEKEY, N_("store a private key to a data object")},
{ "yubikey", cmdYUBIKEY, N_("Yubikey management commands")},
+ { "apdu", cmdAPDU, NULL},
{ NULL, cmdINVCMD, NULL }
};
@@ -3284,7 +3378,7 @@ dispatch_command (card_info_t info, const char *orig_command)
else
{
flush_keyblock_cache ();
- err = scd_apdu (NULL, NULL, NULL, NULL);
+ err = scd_apdu (NULL, NULL, NULL, NULL, NULL);
if (!err)
info->need_sn_cmd = 1;
}
@@ -3312,6 +3406,7 @@ dispatch_command (card_info_t info, const char *orig_command)
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
+ case cmdAPDU: err = cmd_apdu (info, argstr); break;
case cmdINVCMD:
default:
@@ -3503,7 +3598,7 @@ interactive_loop (void)
else
{
flush_keyblock_cache ();
- err = scd_apdu (NULL, NULL, NULL, NULL);
+ err = scd_apdu (NULL, NULL, NULL, NULL, NULL);
if (!err)
info->need_sn_cmd = 1;
}
@@ -3539,6 +3634,7 @@ interactive_loop (void)
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
+ case cmdAPDU: err = cmd_apdu (info, argstr); break;
case cmdINVCMD:
default:
diff --git a/tools/gpg-card.h b/tools/gpg-card.h
index 4bcb6488a..157907a3f 100644
--- a/tools/gpg-card.h
+++ b/tools/gpg-card.h
@@ -208,7 +208,8 @@ gpg_error_t send_apdu (const char *hexapdu, const char *desc,
void release_card_info (card_info_t info);
const char *app_type_string (app_type_t app_type);
-gpg_error_t scd_apdu (const char *hexapdu, unsigned int *r_sw,
+gpg_error_t scd_apdu (const char *hexapdu, const char *options,
+ unsigned int *r_sw,
unsigned char **r_data, size_t *r_datalen);
gpg_error_t scd_switchcard (const char *serialno);