[svn] GnuPG - r4835 - in trunk: . agent g10 scd
svn author wk
cvs at cvs.gnupg.org
Thu Sep 25 12:06:03 CEST 2008
Author: wk
Date: 2008-09-25 12:06:02 +0200 (Thu, 25 Sep 2008)
New Revision: 4835
Modified:
trunk/NEWS
trunk/agent/ChangeLog
trunk/agent/divert-scd.c
trunk/g10/ChangeLog
trunk/g10/call-agent.c
trunk/g10/call-agent.h
trunk/g10/card-util.c
trunk/scd/ChangeLog
trunk/scd/app-openpgp.c
trunk/scd/iso7816.c
trunk/scd/iso7816.h
Log:
Finished support for v2 cards with the exception of secure messaging.
Modified: trunk/agent/ChangeLog
===================================================================
--- trunk/agent/ChangeLog 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/agent/ChangeLog 2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,3 +1,7 @@
+2008-09-25 Werner Koch <wk at g10code.com>
+
+ * divert-scd.c (getpin_cb): Support a Reset Code style PINs..
+
2008-09-03 Werner Koch <wk at g10code.com>
* command.c (parse_keygrip): Use hex2bin.
Modified: trunk/g10/ChangeLog
===================================================================
--- trunk/g10/ChangeLog 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/ChangeLog 2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,3 +1,16 @@
+2008-09-25 Werner Koch <wk at g10code.com>
+
+ * card-util.c (change_pin): Support setting of the reset code.
+
+2008-09-24 Werner Koch <wk at g10code.com>
+
+ * call-agent.h (struct agent_card_info_s): Add field IS_V2.
+ * call-agent.c (learn_status_cb): That that field.
+
+ * card-util.c (change_pin): Rename first arg to UNBLOCK_v2 and use
+ it this way.
+ (card_edit): Add new command UNBLOCK.
+
2008-09-23 David Shaw <dshaw at jabberwocky.com>
* pkclist.c (select_algo_from_prefs): Redo function to rank prefs
Modified: trunk/scd/ChangeLog
===================================================================
--- trunk/scd/ChangeLog 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/ChangeLog 2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,3 +1,22 @@
+2008-09-25 Werner Koch <wk at g10code.com>
+
+ * app-openpgp.c (do_setattr): Do not allow setting of the reset
+ code.
+ (do_change_pin): Allow setting of the reset code.
+
+2008-09-24 Werner Koch <wk at g10code.com>
+
+ * app-openpgp.c (verify_chv3): Set the did_chv3 flag which was
+ accidently removed on 2008-03-26.
+ (verify_chv2): Revert last change.
+ (do_change_pin): Do not change CHV2. Add reset code logic for v2
+ cards.
+ * iso7816.c (iso7816_reset_retry_counter_with_rc): New.
+
+ * app-openpgp.c (add_tlv, build_privkey_template): New.
+ (do_writekey): Support v2 keys and other key lengths than 1024.
+ * iso7816.c (iso7816_put_data_odd): New.
+
2008-09-23 Werner Koch <wk at g10code.com>
* app-openpgp.c (do_sign): Support SHA-2 digests.
@@ -4,6 +23,8 @@
(verify_chv2): No CHV auto-sync for v2 cards.
(do_auth): Allow 2048 bit keys.
(parse_algorithm_attribute): New.
+ (rsa_key_format_t): New.
+ (struct app_local_s): Add struct KEYATTR.
2008-09-23 Marcus Brinkmann <marcus at g10code.com>
@@ -1815,7 +1836,7 @@
the gpg-agent.
- Copyright 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+ Copyright 2002, 2003, 2004, 2005, 2007, 2008 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
Modified: trunk/NEWS
===================================================================
--- trunk/NEWS 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/NEWS 2008-09-25 10:06:02 UTC (rev 4835)
@@ -4,32 +4,36 @@
* New keyserver helper gpg2keys_kdns as generic DNS CERT lookup. Run
with --help for a short description. Requires the ADNS library.
- * New mechanisms "local" and "nodefault" for --auto-key-locate [gpg].
+ * [gpg] New mechanisms "local" and "nodefault" for --auto-key-locate.
Fixed a few problems with this option.
- * [W32] Initialized the socket subsystem for all keyserver helpers.
+ * [w32] Initialized the socket subsystem for all keyserver helpers.
- * [W32] The sysconf directory has been moved from a subdirectory of
+ * [w32] The sysconf directory has been moved from a subdirectory of
the installation directory to %CSIDL_COMMON_APPDATA%/GNU/etc/gnupg.
- * New gpg2 command --locate-keys.
+ * [gpg] New command --locate-keys.
- * New gpg2 options --with-sig-list and --with-sig-check.
+ * [gpg] New options --with-sig-list and --with-sig-check.
- * Made gpgsm's --output option work with --export-secret-key-p12.
+ * [gpgsm] Made --output option work with --export-secret-key-p12.
* gpg-connect-agent accepts commands given as command line arguments.
- * The gpg2 option --fixed-list-mode is now implicitly used and obsolete.
+ * [gpg] The option --fixed-list-mode is now implicitly used and obsolete.
- * New control statement %ask-passphrase for the unattended key
- generation of gpg2.
+ * [gpg] New control statement %ask-passphrase for the unattended key
+ generation.
* gpgsm now uses AES by default.
* gpg-preset-passphrase works again.
+ * Admin PINs are cached again (bug in 2.0.9).
+ * Support for version 2 OpenPGP cards.
+
+
Noteworthy changes in version 2.0.9 (2008-03-26)
------------------------------------------------
Modified: trunk/agent/divert-scd.c
===================================================================
--- trunk/agent/divert-scd.c 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/agent/divert-scd.c 2008-09-25 10:06:02 UTC (rev 4835)
@@ -185,6 +185,7 @@
PIN. If the PIN is not correctly repeated it starts from
all over.
'A' = The PIN is an Admin PIN, SO-PIN, PUK or alike.
+ 'R' = The PIN is a Reset Code.
Example:
@@ -202,6 +203,7 @@
const char *ends, *s;
int any_flags = 0;
int newpin = 0;
+ int resetcode = 0;
const char *again_text = NULL;
const char *prompt = "PIN";
@@ -217,6 +219,11 @@
prompt = _("Admin PIN");
else if (*s == 'N')
newpin = 1;
+ else if (*s == 'R')
+ {
+ prompt = _("Reset Code");
+ resetcode = 1;
+ }
}
info = ends+1;
any_flags = 1;
@@ -272,10 +279,16 @@
pi2->min_digits = 0;
pi2->max_digits = 8;
pi2->max_tries = 1;
- rc = agent_askpin (ctrl, _("Repeat this PIN"), prompt, NULL, pi2);
+ rc = agent_askpin (ctrl,
+ (resetcode?
+ _("Repeat this Reset Code"):
+ _("Repeat this PIN")),
+ prompt, NULL, pi2);
if (!rc && strcmp (pi->pin, pi2->pin))
{
- again_text = N_("PIN not correctly repeated; try again");
+ again_text = (resetcode?
+ N_("Reset Code not correctly repeated; try again"):
+ N_("PIN not correctly repeated; try again"));
xfree (pi2);
xfree (pi);
goto again;
Modified: trunk/g10/call-agent.c
===================================================================
--- trunk/g10/call-agent.c 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/call-agent.c 2008-09-25 10:06:02 UTC (rev 4835)
@@ -264,6 +264,8 @@
{
xfree (parm->serialno);
parm->serialno = store_serialno (line);
+ parm->is_v2 = (strlen (parm->serialno) >= 16
+ && xtoi_2 (parm->serialno+12) >= 2 );
}
else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
{
@@ -758,10 +760,12 @@
/* Change the PIN of an OpenPGP card or reset the retry counter.
CHVNO 1: Change the PIN
- 2: Same as 1
+ 2: For v1 cards: Same as 1.
+ For v2 cards: Reset the PIN using the Reset Code.
3: Change the admin PIN
101: Set a new PIN and reset the retry counter
- 102: Same as 101
+ 102: For v1 cars: Same as 101.
+ For v2 cards: Set a new Reset Code.
SERIALNO is not used.
*/
int
Modified: trunk/g10/call-agent.h
===================================================================
--- trunk/g10/call-agent.h 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/call-agent.h 2008-09-25 10:06:02 UTC (rev 4835)
@@ -49,6 +49,7 @@
int chv1_cached; /* True if a PIN is not required for each
signing. Note that the gpg-agent might cache
it anyway. */
+ int is_v2; /* True if this is a v2 card. */
int chvmaxlen[3]; /* Maximum allowed length of a CHV. */
int chvretry[3]; /* Allowed retries for the CHV; 0 = blocked. */
};
Modified: trunk/g10/card-util.c
===================================================================
--- trunk/g10/card-util.c 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/card-util.c 2008-09-25 10:06:02 UTC (rev 4835)
@@ -51,7 +51,7 @@
/* Change the PIN of a an OpenPGP card. This is an interactive
function. */
void
-change_pin (int chvno, int allow_admin)
+change_pin (int unblock_v2, int allow_admin)
{
struct agent_card_info_s info;
int rc;
@@ -76,8 +76,27 @@
return;
}
- if(!allow_admin)
+
+ if (unblock_v2)
{
+ if (!info.is_v2)
+ log_error (_("This command is only available for version 2 cards\n"));
+ else if (!info.chvretry[1])
+ log_error (_("Reset Code not or not anymore available\n"));
+ else
+ {
+ rc = agent_scd_change_pin (2, info.serialno);
+ if (rc)
+ tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
+ else
+ {
+ write_status (STATUS_SC_OP_SUCCESS);
+ tty_printf ("PIN changed.\n");
+ }
+ }
+ }
+ else if (!allow_admin)
+ {
rc = agent_scd_change_pin (1, info.serialno);
if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
@@ -96,6 +115,7 @@
tty_printf ("1 - change PIN\n"
"2 - unblock PIN\n"
"3 - change Admin PIN\n"
+ "4 - set the Reset Code\n"
"Q - quit\n");
tty_printf ("\n");
@@ -107,6 +127,7 @@
rc = 0;
if (*answer == '1')
{
+ /* Change PIN. */
rc = agent_scd_change_pin (1, info.serialno);
if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
@@ -118,6 +139,7 @@
}
else if (*answer == '2')
{
+ /* Unblock PIN. */
rc = agent_scd_change_pin (101, info.serialno);
if (rc)
tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
@@ -129,6 +151,7 @@
}
else if (*answer == '3')
{
+ /* Change Admin PIN. */
rc = agent_scd_change_pin (3, info.serialno);
if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
@@ -138,6 +161,19 @@
tty_printf ("PIN changed.\n");
}
}
+ else if (*answer == '4')
+ {
+ /* Set a new Reset Code. */
+ rc = agent_scd_change_pin (102, info.serialno);
+ if (rc)
+ tty_printf ("Error setting the Reset Code: %s\n",
+ gpg_strerror (rc));
+ else
+ {
+ write_status (STATUS_SC_OP_SUCCESS);
+ tty_printf ("Reset Code set.\n");
+ }
+ }
else if (*answer == 'q' || *answer == 'Q')
{
break;
@@ -1345,6 +1381,7 @@
cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
+ cmdUNBLOCK,
cmdINVCMD
};
@@ -1375,6 +1412,7 @@
{ "generate", cmdGENERATE, 1, N_("generate new keys")},
{ "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
{ "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")},
+ { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
/* Note, that we do not announce these command yet. */
{ "privatedo", cmdPRIVATEDO, 0, NULL },
{ "writecert", cmdWRITECERT, 1, NULL },
@@ -1644,6 +1682,11 @@
did_checkpin = 0; /* Need to reset it of course. */
break;
+ case cmdUNBLOCK:
+ change_pin (1, allow_admin);
+ did_checkpin = 0; /* Need to reset it of course. */
+ break;
+
case cmdQUIT:
goto leave;
Modified: trunk/scd/app-openpgp.c
===================================================================
--- trunk/scd/app-openpgp.c 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/app-openpgp.c 2008-09-25 10:06:02 UTC (rev 4835)
@@ -19,6 +19,29 @@
* $Id$
*/
+/* Some notes:
+
+ CHV means Card Holder Verification and is nothing else than a PIN
+ or password. That term seems to have been used originally with GSM
+ cards. Version v2 of the specs changes the term to the clearer
+ term PW for password. We use the terms here interchangeable
+ because we do not want to change existing strings i18n wise.
+
+ Version 2 of the specs also drops the separate PW2 which was
+ required in v1 due to ISO requirements. It is now possible to have
+ one physical PW but two reference to it so that they can be
+ individually be verified (e.g. to implement a forced verification
+ for one key). Thus you will noticed the use of PW2 with the verify
+ command but not with change_reference_data because the latter
+ operates directly on the physical PW.
+
+ The Reset Code (RC) as implemented by v2 cards uses the same error
+ counter as the PW2 of v1 cards. By default no RC is set and thus
+ that error counter is set to 0. After setting the RC the error
+ counter will be initialized to 3.
+
+ */
+
#include <config.h>
#include <errno.h>
#include <stdio.h>
@@ -46,6 +69,7 @@
#include "tlv.h"
+/* A table describing the DOs of the card. */
static struct {
int tag;
int constructed;
@@ -91,6 +115,18 @@
};
+/* The format of RSA private keys. */
+typedef enum
+ {
+ RSA_UNKNOWN_FMT,
+ RSA_STD,
+ RSA_STD_N,
+ RSA_CRT,
+ RSA_CRT_N
+ }
+rsa_key_format_t;
+
+
/* One cache item for DOs. */
struct cache_s {
struct cache_s *next;
@@ -150,6 +186,16 @@
unsigned int no_sync:1; /* Do not sync CHV1 and CHV2 */
unsigned int def_chv2:1; /* Use 123456 for CHV2. */
} flags;
+
+ struct
+ {
+ unsigned int n_bits; /* Size of the modulus in bits. The rest
+ of this strucuire is only valid if
+ this is not 0. */
+ unsigned int e_bits; /* Size of the public exponent in bits. */
+ rsa_key_format_t format;
+ } keyattr[3];
+
};
@@ -1495,8 +1541,7 @@
app->did_chv2 = 1;
- if (!app->did_chv1 && !app->force_chv1 && pinvalue
- && !app->app_local->extcap.is_v2)
+ if (!app->did_chv1 && !app->force_chv1 && pinvalue)
{
/* For convenience we verify CHV1 here too. We do this only if
the card is not configured to require a verification before
@@ -1635,6 +1680,7 @@
flush_cache_after_error (app);
return rc;
}
+ app->did_chv3 = 1;
}
return rc;
}
@@ -1673,7 +1719,6 @@
{ "CERT-3", 0x7F21, 3, 0, 1 },
{ "SM-KEY-ENC", 0x00D1, 3, 0, 1 },
{ "SM-KEY-MAC", 0x00D2, 3, 0, 1 },
- { "PW-RESET-CODE",0x00D3, 3, 0, 1 },
{ NULL, 0 }
};
int exmode;
@@ -1758,8 +1803,10 @@
{
int rc = 0;
int chvno = atoi (chvnostr);
+ char *resetcode = NULL;
char *pinvalue;
int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
+ int set_resetcode = 0;
if (reset_mode && chvno == 3)
{
@@ -1768,12 +1815,55 @@
}
else if (reset_mode || chvno == 3)
{
- /* we always require that the PIN is entered. */
+ /* We always require that the PIN is entered. */
app->did_chv3 = 0;
rc = verify_chv3 (app, pincb, pincb_arg);
if (rc)
goto leave;
+
+ if (chvno == 2 && app->app_local->extcap.is_v2)
+ set_resetcode = 1;
}
+ else if (chvno == 2 && app->app_local->extcap.is_v2)
+ {
+ /* There is no PW2 for v2 cards. We use this condition to allow
+ a PW reset using the Reset Code. */
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int remaining;
+
+ relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+ if (!relptr || valuelen < 7)
+ {
+ log_error (_("error retrieving CHV status from card\n"));
+ xfree (relptr);
+ rc = gpg_error (GPG_ERR_CARD);
+ goto leave;
+ }
+ remaining = value[5];
+ xfree (relptr);
+ if (!remaining)
+ {
+ log_error (_("Reset Code not or not anymore available\n"));
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ goto leave;
+ }
+
+ rc = pincb (pincb_arg, _("||Please enter the Reset Code for the card"),
+ &resetcode);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
+ goto leave;
+ }
+ if (strlen (resetcode) < 8)
+ {
+ log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ goto leave;
+ }
+ }
else if (chvno == 1 || chvno == 2)
{
/* CHV1 and CVH2 should always have the same value, thus we
@@ -1802,7 +1892,9 @@
/* TRANSLATORS: Do not translate the "|*|" prefixes but
keep it at the start of the string. We need this elsewhere
to get some infos on the string. */
- rc = pincb (pincb_arg, chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
+ rc = pincb (pincb_arg,
+ set_resetcode? _("|RN|New Reset Code") :
+ chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
&pinvalue);
if (rc)
{
@@ -1810,11 +1902,38 @@
goto leave;
}
- if (reset_mode)
+ if (resetcode)
{
+ char *buffer;
+
+ buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
+ if (!buffer)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ strcpy (stpcpy (buffer, resetcode), pinvalue);
+ rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+ buffer, strlen (buffer));
+ wipememory (buffer, strlen (buffer));
+ xfree (buffer);
+ }
+ }
+ else if (set_resetcode)
+ {
+ if (strlen (pinvalue) < 8)
+ {
+ log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ }
+ else
+ rc = iso7816_put_data (app->slot, 0, 0xD3,
+ pinvalue, strlen (pinvalue));
+ }
+ else if (reset_mode)
+ {
rc = iso7816_reset_retry_counter (app->slot, 0x81,
pinvalue, strlen (pinvalue));
- if (!rc)
+ if (!rc && !app->app_local->extcap.is_v2)
rc = iso7816_reset_retry_counter (app->slot, 0x82,
pinvalue, strlen (pinvalue));
}
@@ -1824,7 +1943,7 @@
{
rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0,
pinvalue, strlen (pinvalue));
- if (!rc)
+ if (!rc && !app->app_local->extcap.is_v2)
rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0,
pinvalue, strlen (pinvalue));
}
@@ -1832,11 +1951,20 @@
rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
pinvalue, strlen (pinvalue));
}
- xfree (pinvalue);
+ if (pinvalue)
+ {
+ wipememory (pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ }
if (rc)
flush_cache_after_error (app);
leave:
+ if (resetcode)
+ {
+ wipememory (resetcode, strlen (resetcode));
+ xfree (resetcode);
+ }
return rc;
}
@@ -1883,7 +2011,163 @@
}
+/* Create a TLV tag and value and store it at BUFFER. Return the length
+ of tag and length. A LENGTH greater than 65535 is truncated. */
+static size_t
+add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
+{
+ unsigned char *p = buffer;
+ assert (tag <= 0xffff);
+ if ( tag > 0xff )
+ *p++ = tag >> 8;
+ *p++ = tag;
+ if (length < 128)
+ *p++ = length;
+ else if (length < 256)
+ {
+ *p++ = 0x81;
+ *p++ = length;
+ }
+ else
+ {
+ if (length > 0xffff)
+ length = 0xffff;
+ *p++ = 0x82;
+ *p++ = length >> 8;
+ *p++ = length;
+ }
+
+ return p - buffer;
+}
+
+
+/* Build the private key template as specified in the OpenPGP specs
+ v2.0 section 4.3.3.7. */
+static gpg_error_t
+build_privkey_template (app_t app, int keyno,
+ const unsigned char *rsa_n, size_t rsa_n_len,
+ const unsigned char *rsa_e, size_t rsa_e_len,
+ const unsigned char *rsa_p, size_t rsa_p_len,
+ const unsigned char *rsa_q, size_t rsa_q_len,
+ unsigned char **result, size_t *resultlen)
+{
+ size_t rsa_e_reqlen;
+ unsigned char privkey[7*(1+3)];
+ size_t privkey_len;
+ unsigned char exthdr[2+2+3];
+ size_t exthdr_len;
+ unsigned char suffix[2+3];
+ size_t suffix_len;
+ unsigned char *tp;
+ size_t datalen;
+ unsigned char *template;
+ size_t template_size;
+
+ *result = NULL;
+ *resultlen = 0;
+
+ switch (app->app_local->keyattr[keyno].format)
+ {
+ case RSA_STD:
+ case RSA_STD_N:
+ break;
+ case RSA_CRT:
+ case RSA_CRT_N:
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ default:
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ /* Get the required length for E. */
+ rsa_e_reqlen = app->app_local->keyattr[keyno].e_bits/8;
+ assert (rsa_e_len <= rsa_e_reqlen);
+
+ /* Build the 7f48 cardholder private key template. */
+ datalen = 0;
+ tp = privkey;
+
+ tp += add_tlv (tp, 0x91, rsa_e_reqlen);
+ datalen += rsa_e_reqlen;
+
+ tp += add_tlv (tp, 0x92, rsa_p_len);
+ datalen += rsa_p_len;
+
+ tp += add_tlv (tp, 0x93, rsa_q_len);
+ datalen += rsa_q_len;
+
+ if (app->app_local->keyattr[keyno].format == RSA_STD_N
+ || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+ {
+ tp += add_tlv (tp, 0x97, rsa_n_len);
+ datalen += rsa_n_len;
+ }
+ privkey_len = tp - privkey;
+
+ /* Build the extended header list without the private key template. */
+ tp = exthdr;
+ *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
+ *tp++ = 0;
+ tp += add_tlv (tp, 0x7f48, privkey_len);
+ exthdr_len = tp - exthdr;
+
+ /* Build the 5f48 suffix of the data. */
+ tp = suffix;
+ tp += add_tlv (tp, 0x5f48, datalen);
+ suffix_len = tp - suffix;
+
+ /* Now concatenate everything. */
+ template_size = (1 + 3 /* 0x4d and len. */
+ + exthdr_len
+ + privkey_len
+ + suffix_len
+ + datalen);
+ tp = template = xtrymalloc_secure (template_size);
+ if (!template)
+ return gpg_error_from_syserror ();
+
+ tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
+ memcpy (tp, exthdr, exthdr_len);
+ tp += exthdr_len;
+ memcpy (tp, privkey, privkey_len);
+ tp += privkey_len;
+ memcpy (tp, suffix, suffix_len);
+ tp += suffix_len;
+
+ memcpy (tp, rsa_e, rsa_e_len);
+ if (rsa_e_len < rsa_e_reqlen)
+ {
+ /* Right justify E. */
+ memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
+ memset (tp, 0, rsa_e_reqlen - rsa_e_len);
+ }
+ tp += rsa_e_reqlen;
+
+ memcpy (tp, rsa_p, rsa_p_len);
+ tp += rsa_p_len;
+
+ memcpy (tp, rsa_q, rsa_q_len);
+ tp += rsa_q_len;
+
+ if (app->app_local->keyattr[keyno].format == RSA_STD_N
+ || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+ {
+ memcpy (tp, rsa_n, rsa_n_len);
+ tp += rsa_n_len;
+ }
+
+ /* Sanity check. We don't know the exact length because we
+ allocated 3 bytes for the first length header. */
+ assert (tp - template <= template_size);
+
+ *result = template;
+ *resultlen = tp - template;
+ return 0;
+}
+
+
+
/* Handle the WRITEKEY command for OpenPGP. This function expects a
canonical encoded S-expression with the secret key in KEYDATA and
its length (for assertions) in KEYDATALEN. KEYID needs to be the
@@ -1910,6 +2194,7 @@
const unsigned char *rsa_q = NULL;
size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
unsigned int nbits;
+ unsigned int maxbits;
unsigned char *template = NULL;
unsigned char *tp;
size_t template_len;
@@ -2049,92 +2334,133 @@
err = gpg_error (GPG_ERR_INV_VALUE);
goto leave;
}
+
+ maxbits = app->app_local->keyattr[keyno].n_bits;
nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
- if (nbits != 1024)
+ if (nbits != maxbits)
{
- log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+ log_error (_("RSA modulus missing or not of size %d bits\n"),
+ (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
+
+ maxbits = app->app_local->keyattr[keyno].e_bits;
+ if (maxbits > 32 && !app->app_local->extcap.is_v2)
+ maxbits = 32; /* Our code for v1 does only support 32 bits. */
nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
- if (nbits < 2 || nbits > 32)
+ if (nbits < 2 || nbits > maxbits)
{
log_error (_("RSA public exponent missing or larger than %d bits\n"),
- 32);
+ (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
+
+ maxbits = app->app_local->keyattr[keyno].n_bits/2;
nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
- if (nbits != 512)
+ if (nbits != maxbits)
{
- log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
+ log_error (_("RSA prime %s missing or not of size %d bits\n"),
+ "P", (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
- if (nbits != 512)
+ if (nbits != maxbits)
{
- log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
+ log_error (_("RSA prime %s missing or not of size %d bits\n"),
+ "Q", (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
-
- /* Build the private key template as described in section 4.3.3.6 of
- the OpenPGP card specs:
- 0xC0 <length> public exponent
- 0xC1 <length> prime p
- 0xC2 <length> prime q
- */
- assert (rsa_e_len <= 4);
- template_len = (1 + 1 + 4
- + 1 + 1 + rsa_p_len
- + 1 + 1 + rsa_q_len);
- template = tp = xtrymalloc_secure (template_len);
- if (!template)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- *tp++ = 0xC0;
- *tp++ = 4;
- memcpy (tp, rsa_e, rsa_e_len);
- if (rsa_e_len < 4)
- {
- /* Right justify E. */
- memmove (tp+4-rsa_e_len, tp, rsa_e_len);
- memset (tp, 0, 4-rsa_e_len);
- }
- tp += 4;
-
- *tp++ = 0xC1;
- *tp++ = rsa_p_len;
- memcpy (tp, rsa_p, rsa_p_len);
- tp += rsa_p_len;
-
- *tp++ = 0xC2;
- *tp++ = rsa_q_len;
- memcpy (tp, rsa_q, rsa_q_len);
- tp += rsa_q_len;
-
- assert (tp - template == template_len);
-
-
- /* Obviously we need to remove the cached public key. */
+ /* We need to remove the cached public key. */
xfree (app->app_local->pk[keyno].key);
app->app_local->pk[keyno].key = NULL;
app->app_local->pk[keyno].keylen = 0;
app->app_local->pk[keyno].read_done = 0;
- /* Prepare for storing the key. */
- err = verify_chv3 (app, pincb, pincb_arg);
- if (err)
- goto leave;
- /* Store the key. */
- err = iso7816_put_data (app->slot, 0,
- (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
- template, template_len);
+ if (app->app_local->extcap.is_v2)
+ {
+ /* Build the private key template as described in section 4.3.3.7 of
+ the OpenPGP card specs version 2.0. */
+ int exmode;
+
+ err = build_privkey_template (app, keyno,
+ rsa_n, rsa_n_len,
+ rsa_e, rsa_e_len,
+ rsa_p, rsa_p_len,
+ rsa_q, rsa_q_len,
+ &template, &template_len);
+ if (err)
+ goto leave;
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ goto leave;
+
+ /* Store the key. */
+ if (app->app_local->cardcap.cmd_chaining && template_len > 254)
+ exmode = -254;
+ else
+ exmode = 0;
+ err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
+ template, template_len);
+ }
+ else
+ {
+ /* Build the private key template as described in section 4.3.3.6 of
+ the OpenPGP card specs version 1.1:
+ 0xC0 <length> public exponent
+ 0xC1 <length> prime p
+ 0xC2 <length> prime q
+ */
+ assert (rsa_e_len <= 4);
+ template_len = (1 + 1 + 4
+ + 1 + 1 + rsa_p_len
+ + 1 + 1 + rsa_q_len);
+ template = tp = xtrymalloc_secure (template_len);
+ if (!template)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ *tp++ = 0xC0;
+ *tp++ = 4;
+ memcpy (tp, rsa_e, rsa_e_len);
+ if (rsa_e_len < 4)
+ {
+ /* Right justify E. */
+ memmove (tp+4-rsa_e_len, tp, rsa_e_len);
+ memset (tp, 0, 4-rsa_e_len);
+ }
+ tp += 4;
+
+ *tp++ = 0xC1;
+ *tp++ = rsa_p_len;
+ memcpy (tp, rsa_p, rsa_p_len);
+ tp += rsa_p_len;
+
+ *tp++ = 0xC2;
+ *tp++ = rsa_q_len;
+ memcpy (tp, rsa_q, rsa_q_len);
+ tp += rsa_q_len;
+
+ assert (tp - template == template_len);
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ goto leave;
+
+ /* Store the key. */
+ err = iso7816_put_data (app->slot, 0,
+ (app->card_version > 0x0007? 0xE0:0xE9)+keyno,
+ template, template_len);
+ }
if (err)
{
log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
@@ -2891,8 +3217,8 @@
}
-/* Read and parse the algorithm attributes for KEYNO. KEYNO must be
- in the range 0..2. */
+/* Parse and optionally show the algorithm attributes for KEYNO.
+ KEYNO must be in the range 0..2. */
static void
parse_algorithm_attribute (app_t app, int keyno)
{
@@ -2903,6 +3229,8 @@
assert (keyno >=0 && keyno <= 2);
+ app->app_local->keyattr[keyno].n_bits = 0;
+
relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
if (!relptr)
{
@@ -2916,21 +3244,33 @@
return;
}
- log_info ("Key-Attr-%s ..: ", desc[keyno]);
+ if (opt.verbose)
+ log_info ("Key-Attr-%s ..: ", desc[keyno]);
if (*buffer == 1 && (buflen == 5 || buflen == 6))
{
- log_printf ("RSA, n=%d, e=%d",
- (buffer[1]<<8 | buffer[2]),
- (buffer[3]<<8 | buffer[4]));
- if (buflen == 6)
- log_printf (", format=%s",
- buffer[5] == 0? "std" :
- buffer[5] == 1? "std+n" :
- buffer[5] == 2? "crt" :
- buffer[5] == 2? "crt+n" : "?");
- log_printf ("\n");
+ app->app_local->keyattr[keyno].n_bits = (buffer[1]<<8 | buffer[2]);
+ app->app_local->keyattr[keyno].e_bits = (buffer[3]<<8 | buffer[4]);
+ app->app_local->keyattr[keyno].format = 0;
+ if (buflen < 6)
+ app->app_local->keyattr[keyno].format = RSA_STD;
+ else
+ app->app_local->keyattr[keyno].format = (buffer[5] == 0? RSA_STD :
+ buffer[5] == 1? RSA_STD_N :
+ buffer[5] == 2? RSA_CRT :
+ buffer[5] == 3? RSA_CRT_N :
+ RSA_UNKNOWN_FMT);
+
+ if (opt.verbose)
+ log_printf
+ ("RSA, n=%u, e=%u, fmt=%s\n",
+ app->app_local->keyattr[keyno].n_bits,
+ app->app_local->keyattr[keyno].e_bits,
+ app->app_local->keyattr[keyno].format == RSA_STD? "std" :
+ app->app_local->keyattr[keyno].format == RSA_STD_N?"std+n":
+ app->app_local->keyattr[keyno].format == RSA_CRT? "crt" :
+ app->app_local->keyattr[keyno].format == RSA_CRT_N?"crt+n":"?");
}
- else
+ else if (opt.verbose)
log_printhex ("", buffer, buflen);
xfree (relptr);
@@ -3057,12 +3397,9 @@
if (opt.verbose)
show_caps (app->app_local);
- if (opt.verbose)
- {
- parse_algorithm_attribute (app, 0);
- parse_algorithm_attribute (app, 1);
- parse_algorithm_attribute (app, 2);
- }
+ parse_algorithm_attribute (app, 0);
+ parse_algorithm_attribute (app, 1);
+ parse_algorithm_attribute (app, 2);
if (opt.verbose > 1)
dump_all_do (slot);
Modified: trunk/scd/iso7816.c
===================================================================
--- trunk/scd/iso7816.c 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/iso7816.c 2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,5 +1,5 @@
/* iso7816.c - ISO 7816 commands
- * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -335,6 +335,7 @@
if (!newchv || !newchvlen )
return gpg_error (GPG_ERR_INV_VALUE);
+ /* FIXME: The keypad mode has not yet been tested. */
if (pininfo && pininfo->mode)
sw = apdu_send_simple_kp (slot, 0x00, CMD_RESET_RETRY_COUNTER,
2, chvno, newchvlen, newchv,
@@ -350,6 +351,21 @@
gpg_error_t
+iso7816_reset_retry_counter_with_rc (int slot, int chvno,
+ const char *data, size_t datalen)
+{
+ int sw;
+
+ if (!data || !datalen )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
+ 0, chvno, datalen, data);
+ return map_sw (sw);
+}
+
+
+gpg_error_t
iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen)
{
@@ -404,6 +420,19 @@
return map_sw (sw);
}
+/* Same as iso7816_put_data but uses an odd instrcution byte. */
+gpg_error_t
+iso7816_put_data_odd (int slot, int extended_mode, int tag,
+ const unsigned char *data, size_t datalen)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA+1,
+ ((tag >> 8) & 0xff), (tag & 0xff),
+ datalen, (const char*)data);
+ return map_sw (sw);
+}
+
/* Manage Security Environment. This is a weird operation and there
is no easy abstraction for it. Furthermore, some card seem to have
a different interpreation of 7816-8 and thus we resort to let the
Modified: trunk/scd/iso7816.h
===================================================================
--- trunk/scd/iso7816.h 2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/iso7816.h 2008-09-25 10:06:02 UTC (rev 4835)
@@ -77,10 +77,15 @@
const char *newchv,
size_t newchvlen,
iso7816_pininfo_t *pininfo);
+gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
+ const char *data,
+ size_t datalen);
gpg_error_t iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen);
+gpg_error_t iso7816_put_data_odd (int slot, int extended_mode, int tag,
+ const unsigned char *data, size_t datalen);
gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
const unsigned char *data,
size_t datalen);
More information about the Gnupg-commits
mailing list