1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

Finished support for v2 cards with the exception of secure messaging.

This commit is contained in:
Werner Koch 2008-09-25 10:06:02 +00:00
parent 0d71795aae
commit 96f16f736e
11 changed files with 578 additions and 104 deletions

22
NEWS
View File

@ -4,31 +4,35 @@ Noteworthy changes in version 2.0.10 (unreleased)
* 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)
------------------------------------------------

View File

@ -1,3 +1,7 @@
2008-09-25 Werner Koch <wk@g10code.com>
* divert-scd.c (getpin_cb): Support a Reset Code style PINs..
2008-09-03 Werner Koch <wk@g10code.com>
* command.c (parse_keygrip): Use hex2bin.

View File

@ -185,6 +185,7 @@ encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
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;

View File

@ -1,3 +1,16 @@
2008-09-25 Werner Koch <wk@g10code.com>
* card-util.c (change_pin): Support setting of the reset code.
2008-09-24 Werner Koch <wk@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@jabberwocky.com>
* pkclist.c (select_algo_from_prefs): Redo function to rank prefs

View File

@ -264,6 +264,8 @@ learn_status_cb (void *opaque, const char *line)
{
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 @@ agent_scd_pkdecrypt (const char *serialno,
/* 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

View File

@ -49,6 +49,7 @@ struct agent_card_info_s
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. */
};

View File

@ -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,7 +76,26 @@ change_pin (int chvno, int allow_admin)
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)
@ -96,6 +115,7 @@ change_pin (int chvno, int allow_admin)
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 @@ change_pin (int chvno, int allow_admin)
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 @@ change_pin (int chvno, int allow_admin)
}
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 @@ change_pin (int chvno, int allow_admin)
}
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 @@ change_pin (int chvno, int allow_admin)
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 @@ enum cmdids
cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdUNBLOCK,
cmdINVCMD
};
@ -1375,6 +1412,7 @@ static struct
{ "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 @@ card_edit (strlist_t commands)
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;

View File

@ -1,9 +1,30 @@
2008-09-25 Werner Koch <wk@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@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@g10code.com>
* app-openpgp.c (do_sign): Support SHA-2 digests.
(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@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

View File

@ -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 @@ static struct {
};
/* 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 @@ struct app_local_s {
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 @@ verify_chv2 (app_t app,
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 @@ verify_chv3 (app_t app,
flush_cache_after_error (app);
return rc;
}
app->did_chv3 = 1;
}
return rc;
}
@ -1673,7 +1719,6 @@ do_setattr (app_t app, const char *name,
{ "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 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
{
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,11 +1815,54 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
}
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)
{
@ -1802,7 +1892,9 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
/* 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 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
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 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
{
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 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
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,6 +2011,162 @@ does_key_exist (app_t app, int keyidx, int force)
}
/* 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
@ -1910,6 +2194,7 @@ do_writekey (app_t app, ctrl_t ctrl,
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 @@ do_writekey (app_t app, ctrl_t ctrl,
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 @@ parse_historical (struct app_local_s *apploc,
}
/* 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 @@ parse_algorithm_attribute (app_t app, int keyno)
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 @@ parse_algorithm_attribute (app_t app, int keyno)
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 @@ app_select_openpgp (app_t app)
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);

View File

@ -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 @@ iso7816_reset_retry_counter_kp (int slot, int chvno,
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,
@ -349,6 +350,21 @@ iso7816_reset_retry_counter_kp (int slot, int chvno,
}
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 @@ iso7816_put_data (int slot, int extended_mode, int tag,
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

View File

@ -77,10 +77,15 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
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);