From ab4ea45f54006eba55db11263431c4c0c4f557dc Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 6 Nov 2012 14:39:22 +0100 Subject: [PATCH] Allow decryption with card keys > 3072 bit * scd/command.c (MAXLEN_SETDATA): New. (cmd_setdata): Add option --append. * g10/call-agent.c (agent_scd_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. --- g10/call-agent.c | 22 ++++++++++++++-------- scd/app-openpgp.c | 10 ++++++++++ scd/command.c | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/g10/call-agent.c b/g10/call-agent.c index cded773c8..373d8c9b7 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1034,7 +1034,7 @@ agent_scd_pksign (const char *serialno, int hashalgo, /* Decrypt INDATA of length INDATALEN using the card identified by - SERIALNO. Return the plaintext in a nwly allocated buffer stored + SERIALNO. Return the plaintext in a newly allocated buffer stored at the address of R_BUF. Note, we currently support only RSA or more exactly algorithms @@ -1058,20 +1058,26 @@ agent_scd_pkdecrypt (const char *serialno, return rc; /* FIXME: use secure memory where appropriate */ - if (indatalen*2 + 50 > DIM(line)) - return gpg_error (GPG_ERR_GENERAL); rc = select_openpgp (serialno); if (rc) return rc; - sprintf (line, "SCD SETDATA "); - p = line + strlen (line); - for (i=0; i < indatalen ; i++, p += 2 ) - sprintf (p, "%02X", indata[i]); - rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + for (len = 0; len < indatalen;) + { + p = stpcpy (line, "SCD 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 (agent_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; + } init_membuf (&data, 1024); snprintf (line, DIM(line)-1, "SCD PKDECRYPT %s", serialno); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index ff26b36e4..141b2b740 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 { @@ -3462,6 +3464,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; @@ -3749,6 +3757,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 6053fc6af..3ce4a5709 100644 --- a/scd/command.c +++ b/scd/command.c @@ -46,6 +46,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 @@ -820,17 +823,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); @@ -844,14 +854,30 @@ cmd_setdata (assuan_context_t ctx, char *line) if ((n&1)) return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits"); n /= 2; + 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); + 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; }