From 96f16f736e97992c985f01c5e93bf825fdcd5707 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 25 Sep 2008 10:06:02 +0000 Subject: [PATCH] Finished support for v2 cards with the exception of secure messaging. --- NEWS | 22 +- agent/ChangeLog | 4 + agent/divert-scd.c | 17 +- g10/ChangeLog | 13 ++ g10/call-agent.c | 8 +- g10/call-agent.h | 1 + g10/card-util.c | 47 ++++- scd/ChangeLog | 23 +- scd/app-openpgp.c | 511 +++++++++++++++++++++++++++++++++++++-------- scd/iso7816.c | 31 ++- scd/iso7816.h | 5 + 11 files changed, 578 insertions(+), 104 deletions(-) diff --git a/NEWS b/NEWS index 7b46d9977..853a61cb8 100644 --- a/NEWS +++ b/NEWS @@ -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) ------------------------------------------------ diff --git a/agent/ChangeLog b/agent/ChangeLog index 0a5e6afb4..9711cd171 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,7 @@ +2008-09-25 Werner Koch + + * divert-scd.c (getpin_cb): Support a Reset Code style PINs.. + 2008-09-03 Werner Koch * command.c (parse_keygrip): Use hex2bin. diff --git a/agent/divert-scd.c b/agent/divert-scd.c index eac3b59c3..b19302496 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -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; diff --git a/g10/ChangeLog b/g10/ChangeLog index 3780be777..28a6cd9ce 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,16 @@ +2008-09-25 Werner Koch + + * card-util.c (change_pin): Support setting of the reset code. + +2008-09-24 Werner Koch + + * 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 * pkclist.c (select_algo_from_prefs): Redo function to rank prefs diff --git a/g10/call-agent.c b/g10/call-agent.c index 9767f040f..29d75a59e 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -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 diff --git a/g10/call-agent.h b/g10/call-agent.h index 63b460a90..ee69793fa 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -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. */ }; diff --git a/g10/card-util.c b/g10/card-util.c index 2d00bb3ac..afde4cb68 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -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; diff --git a/scd/ChangeLog b/scd/ChangeLog index 664b1c251..572959c70 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,9 +1,30 @@ +2008-09-25 Werner Koch + + * 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 + + * 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 * 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 @@ -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 diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 3a79b56b0..ca17024a5 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -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 #include #include @@ -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 public exponent - 0xC1 prime p - 0xC2 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 public exponent + 0xC1 prime p + 0xC2 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); diff --git a/scd/iso7816.c b/scd/iso7816.c index 75a8f4a6a..2286090b6 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -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 diff --git a/scd/iso7816.h b/scd/iso7816.h index 19568184d..284751137 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -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);