From 905b6a36d3ca21b2f619721e1de892398e5eb759 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 6 Nov 2012 12:02:25 +0100 Subject: [PATCH] Allow decryption with card keys > 3072 bits * scd/command.c (MAXLEN_SETDATA): New. (cmd_setdata): Add option --append. * agent/call-scd.c (agent_card_pkdecrypt): Use new option for long data. * scd/app-openpgp.c (struct app_local_s): Add field manufacturer. (app_select_openpgp): Store manufacturer. (do_decipher): Print a note for broken cards. -- Please note that I was not able to run a full test because I only have broken cards (S/N < 346) available. --- agent/call-scd.c | 25 +++++++++++++++---------- scd/app-openpgp.c | 10 ++++++++++ scd/command.c | 40 +++++++++++++++++++++++++++++++++------- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/agent/call-scd.c b/agent/call-scd.c index 8fc00542e..2bda3779d 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -926,17 +926,22 @@ agent_card_pkdecrypt (ctrl_t ctrl, return rc; /* FIXME: use secure memory where appropriate */ - if (indatalen*2 + 50 > DIM(line)) - return unlock_scd (ctrl, gpg_error (GPG_ERR_GENERAL)); - sprintf (line, "SETDATA "); - p = line + strlen (line); - for (i=0; i < indatalen ; i++, p += 2 ) - sprintf (p, "%02X", indata[i]); - rc = assuan_transact (ctrl->scd_local->ctx, line, - NULL, NULL, NULL, NULL, NULL, NULL); - if (rc) - return unlock_scd (ctrl, rc); + for (len = 0; len < indatalen;) + { + p = stpcpy (line, "SETDATA "); + if (len) + p = stpcpy (p, "--append "); + for (i=0; len < indatalen && (i*2 < DIM(line)-50); i++, len++) + { + sprintf (p, "%02X", indata[len]); + p += 2; + } + rc = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_scd (ctrl, rc); + } init_membuf (&data, 1024); inqparm.ctx = ctrl->scd_local->ctx; diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 9c8ef59cb..5a9214c1c 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -158,6 +158,8 @@ struct app_local_s { unsigned char status_indicator; /* The card status indicator. */ + unsigned int manufacturer:16; /* Manufacturer ID from the s/n. */ + /* Keep track of the ISO card capabilities. */ struct { @@ -3474,6 +3476,12 @@ do_decipher (app_t app, const char *keyidstr, indata, indatalen, le_value, padind, outdata, outdatalen); xfree (fixbuf); + + if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */ + && app->app_local->manufacturer == 5 + && app->card_version == 0x0200) + log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)" + " do not work with encryption keys > 2048 bits\n"); } return rc; @@ -3761,6 +3769,8 @@ app_select_openpgp (app_t app) goto leave; } + app->app_local->manufacturer = manufacturer; + if (app->card_version >= 0x0200) app->app_local->extcap.is_v2 = 1; diff --git a/scd/command.c b/scd/command.c index 102715804..17da5b7ae 100644 --- a/scd/command.c +++ b/scd/command.c @@ -48,6 +48,9 @@ /* Maximum allowed size of key data as used in inquiries. */ #define MAXLEN_KEYDATA 4096 +/* Maximum allowed total data size for SETDATA. */ +#define MAXLEN_SETDATA 4096 + /* Maximum allowed size of certificate data as used in inquiries. */ #define MAXLEN_CERTDATA 16384 @@ -848,17 +851,24 @@ cmd_readkey (assuan_context_t ctx, char *line) static const char hlp_setdata[] = - "SETDATA \n" + "SETDATA [--append] \n" "\n" - "The client should use this command to tell us the data he want to sign."; + "The client should use this command to tell us the data he want to sign.\n" + "With the option --append, the data is appended to the data set by a\n" + "previous SETDATA command."; static gpg_error_t cmd_setdata (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - int n; + int append; + int n, i, off; char *p; unsigned char *buf; + append = (ctrl->in_data.value && has_option (line, "--append")); + + line = skip_options (line); + if (locked_session && locked_session != ctrl->server_local) return gpg_error (GPG_ERR_LOCKED); @@ -872,15 +882,31 @@ cmd_setdata (assuan_context_t ctx, char *line) if ((n&1)) return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits"); n /= 2; - buf = xtrymalloc (n); + if (append) + { + if (ctrl->in_data.valuelen + n > MAXLEN_SETDATA) + return set_error (GPG_ERR_TOO_LARGE, + "limit on total size of data reached"); + buf = xtrymalloc (ctrl->in_data.valuelen + n); + } + else + buf = xtrymalloc (n); if (!buf) return out_of_core (); + if (append) + { + memcpy (buf, ctrl->in_data.value, ctrl->in_data.valuelen); + off = ctrl->in_data.valuelen; + } + else + off = 0; + for (p=line, i=0; i < n; p += 2, i++) + buf[off+i] = xtoi_2 (p); + xfree (ctrl->in_data.value); ctrl->in_data.value = buf; - ctrl->in_data.valuelen = n; - for (p=line, n=0; n < ctrl->in_data.valuelen; p += 2, n++) - buf[n] = xtoi_2 (p); + ctrl->in_data.valuelen = off+n; return 0; }