diff --git a/NEWS b/NEWS index db07c2469..da633dbe2 100644 --- a/NEWS +++ b/NEWS @@ -7,7 +7,13 @@ Noteworthy changes in version 1.3.7 * Support for the OpenPGP smartcard is now enabled by default. Use the option --disable-card-support to build without support - for smartcards. + for smartcards. + + * New command "addcardkey" in the key edit menu to add subkeys to + a smartcard. The serial number of the card is show in secret + key listings. + + * -K may now be used as an alias for --list-secret-keys. * HTTP Basic authentication is now supported for all HKP and HTTP keyserver functions, either through a proxy or via direct diff --git a/TODO b/TODO index d51ce1b84..486926232 100644 --- a/TODO +++ b/TODO @@ -113,6 +113,12 @@ * Add the NEWSIG status. + * When generating a key onh the card we should try to also set the + display name or provide the display name as a default in the key + generation. The problem however is that the display name must be + given with an indication of the surname and the usable characters + are also restricted. + Things we won't do ------------------ diff --git a/doc/ChangeLog b/doc/ChangeLog index 47332c320..deb9a7730 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2004-09-20 Werner Koch + + * gpg.sgml: Document -K. + 2004-09-16 David Shaw * DETAILS: Document the 'spk' signature subpacket record. Add diff --git a/doc/gpg.sgml b/doc/gpg.sgml index f74bd43e0..f5bb0695f 100644 --- a/doc/gpg.sgml +++ b/doc/gpg.sgml @@ -252,7 +252,7 @@ scripts and other programs. ---list-secret-keys &OptParmNames; +-K, --list-secret-keys &OptParmNames; List all keys from the secret keyrings, or just the ones given on the command line. A '#' after the letters 'sec' means that the secret key @@ -409,6 +409,11 @@ Revoke a user id. addkey Add a subkey to this key. + + addcardkey + +Generate a key on a card and add it +to this key. delkey diff --git a/g10/ChangeLog b/g10/ChangeLog index b4871247d..0b8837316 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,16 @@ 2004-09-20 Werner Koch + * g10.c: Make -K an alias for --list-secret-keys. + + * keylist.c (print_card_serialno): New. Taken from gnupg 1.9.11. + (list_keyblock_print): Make use of it. + * keyedit.c (show_key_with_all_names): Print the card S/N. + + * keyedit.c (keyedit_menu): New command ADDCARDKEY. + * card-util.c (card_generate_subkey): New. + * keygen.c (generate_card_subkeypair): New. + (gen_card_key): New arg IS_PRIMARY; changed all callers. + * cardglue.c (open_card): Use shutdown code if possible. (check_card_serialno): Ditto. diff --git a/g10/card-util.c b/g10/card-util.c index 597faba6d..194bd83e2 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -828,6 +828,120 @@ generate_card_keys (const char *serialno) } } + +/* This fucntion is used by the key edit menu to generate an arbitrary + subkey. */ +int +card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock) +{ + struct agent_card_info_s info; + int okay = 0; + int forced_chv1 = 0; + int rc; + int keyno; + + memset (&info, 0, sizeof info); + rc = agent_scd_getattr ("SERIALNO", &info); + if (rc || !info.serialno || strncmp (info.serialno, "D27600012401", 12) + || strlen (info.serialno) != 32 ) + { + log_error (_("cannot generate key: %s\n"), + rc ? gpg_strerror (rc) : _("not an OpenPGP card")); + goto leave; + } + rc = agent_scd_getattr ("KEY-FPR", &info); + if (!rc) + rc = agent_scd_getattr ("CHV-STATUS", &info); + if (rc) + { + log_error ("error getting current key info: %s\n", gpg_strerror (rc)); + goto leave; + } + + tty_fprintf (NULL, "Signature key ....:"); + print_sha1_fpr (NULL, info.fpr1valid? info.fpr1:NULL); + tty_fprintf (NULL, "Encryption key....:"); + print_sha1_fpr (NULL, info.fpr2valid? info.fpr2:NULL); + tty_fprintf (NULL, "Authentication key:"); + print_sha1_fpr (NULL, info.fpr3valid? info.fpr3:NULL); + tty_printf ("\n"); + + tty_printf (_("Please select the type of key to generate:\n")); + + tty_printf (_(" (1) Signature key\n")); + tty_printf (_(" (2) Encryption key\n")); + tty_printf (_(" (3) Authentication key\n")); + + for (;;) + { + char *answer = cpr_get ("cardedit.genkeys.subkeytype", + _("Your selection? ")); + cpr_kill_prompt(); + if (*answer == CONTROL_D) + { + xfree (answer); + goto leave; + } + keyno = *answer? atoi(answer): 0; + xfree(answer); + if (keyno >= 1 && keyno <= 3) + break; /* Okay. */ + tty_printf(_("Invalid selection.\n")); + } + + if ((keyno == 1 && info.fpr1valid) + || (keyno == 2 && info.fpr2valid) + || (keyno == 3 && info.fpr3valid)) + { + tty_printf ("\n"); + log_info ("WARNING: such a key has already been stored on the card!\n"); + tty_printf ("\n"); + if ( !cpr_get_answer_is_yes( "cardedit.genkeys.replace_key", + _("Replace existing key? "))) + goto leave; + } + + forced_chv1 = !info.chv1_cached; + if (forced_chv1) + { /* Switch of the forced mode so that during key generation we + don't get bothered with PIN queries for each + self-signature. */ + rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1); + if (rc) + { + log_error ("error clearing forced signature PIN flag: %s\n", + gpg_strerror (rc)); + forced_chv1 = 0; + goto leave; + } + } + + /* Check the PIN now, so that we won't get asked later for each + binding signature. */ + rc = agent_scd_checkpin (info.serialno); + if (rc) + log_error ("error checking the PIN: %s\n", gpg_strerror (rc)); + else + okay = generate_card_subkeypair (pub_keyblock, sec_keyblock, + keyno, info.serialno); + + leave: + agent_release_card_info (&info); + if (forced_chv1) + { /* Switch back to forced state. */ + rc = agent_scd_setattr ("CHV-STATUS-1", "", 1); + if (rc) + { + log_error ("error setting forced signature PIN flag: %s\n", + gpg_strerror (rc)); + return okay; + } + } + return okay; +} + + + /* Menu to edit all user changeable values on an OpenPGP card. Only Key creation is not handled here. */ void diff --git a/g10/cardglue.c b/g10/cardglue.c index 5faa41bad..cff025cb3 100644 --- a/g10/cardglue.c +++ b/g10/cardglue.c @@ -812,8 +812,8 @@ agent_scd_change_pin (int chvno) pin_cb, NULL); } -/* Perform a CHECKPIN operation. SERIALNO should be the seriial - number of the card - optioanlly followed by the fingerprint; +/* Perform a CHECKPIN operation. SERIALNO should be the serial + number of the card - optionally followed by the fingerprint; however the fingerprint is ignored here. */ int agent_scd_checkpin (const char *serialnobuf) diff --git a/g10/g10.c b/g10/g10.c index 3fd6c9aad..66692f79e 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -80,6 +80,7 @@ enum cmd_and_opt_values oVerbose = 'v', oCompress = 'z', oSetNotation = 'N', + aListSecretKeys = 'K', oBatch = 500, oMaxOutput, oSigNotation, @@ -114,7 +115,6 @@ enum cmd_and_opt_values aVerifyFiles, aListKeys, aListSigs, - aListSecretKeys, aSendKeys, aRecvKeys, aSearchKeys, diff --git a/g10/keyedit.c b/g10/keyedit.c index 288d8ba1c..014f3a8c0 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1191,6 +1191,7 @@ keyedit_menu( const char *username, STRLIST locusr, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdUPDPREF, cmdPREFKS, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST, + cmdADDCARDKEY, cmdNOP }; static struct { const char *name; enum cmdids id; @@ -1223,6 +1224,7 @@ keyedit_menu( const char *username, STRLIST locusr, /* delphoto is really deluid in disguise */ { N_("delphoto"), cmdDELUID , 0,1, NULL }, { N_("addkey") , cmdADDKEY , 1,1, N_("add a secondary key") }, + { N_("addcardkey"), cmdADDCARDKEY , 1,1, N_("add a key to a smartcard") }, { N_("delkey") , cmdDELKEY , 0,1, N_("delete a secondary key") }, { N_("addrevoker"),cmdADDREVOKER,1,1, N_("add a revocation key") }, { N_("delsig") , cmdDELSIG , 0,1, N_("delete signatures") }, @@ -1530,6 +1532,16 @@ keyedit_menu( const char *username, STRLIST locusr, } break; +#ifdef ENABLE_CARD_SUPPORT + case cmdADDCARDKEY: + if (card_generate_subkey (keyblock, sec_keyblock)) { + redisplay = 1; + sec_modified = modified = 1; + merge_keys_and_selfsig( sec_keyblock ); + merge_keys_and_selfsig( keyblock ); + } + break; +#endif /* ENABLE_CARD_SUPPORT */ case cmdDELKEY: { int n1; @@ -2210,6 +2222,27 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker, tty_printf(" "); tty_printf(_("expires: %s"),expirestr_from_sk(sk)); tty_printf("\n"); + if (sk->is_protected && sk->protect.s2k.mode == 1002) + { + tty_printf(" "); + tty_printf(_("card-no: ")); + if (sk->protect.ivlen == 16 + && !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01", 6)) + { /* This is an OpenPGP card. */ + for (i=8; i < 14; i++) + { + if (i == 10) + tty_printf (" "); + tty_printf ("%02X", sk->protect.iv[i]); + } + } + else + { /* Something is wrong: Print all. */ + for (i=0; i < sk->protect.ivlen; i++) + tty_printf ("%02X", sk->protect.iv[i]); + } + tty_printf ("\n"); + } } else if( with_subkeys && node->pkt->pkttype == PKT_SIGNATURE && node->pkt->pkt.signature->sig_class == 0x28 ) { @@ -2555,7 +2588,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo) /**************** - * Remove all selceted userids from the keyrings + * Remove all selected userids from the keyrings */ static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock ) diff --git a/g10/keygen.c b/g10/keygen.c index 6bf1c0cbd..44cfc00ec 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -117,7 +117,8 @@ static int mdc_available,ks_modify; static void do_generate_keypair( struct para_data_s *para, struct output_control_s *outctrl, int card ); static int write_keyblock( IOBUF out, KBNODE node ); -static int gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, +static int gen_card_key (int algo, int keyno, int is_primary, + KBNODE pub_root, KBNODE sec_root, u32 expireval, struct para_data_s *para); static void @@ -2481,7 +2482,7 @@ do_generate_keypair( struct para_data_s *para, } else { - rc = gen_card_key (PUBKEY_ALGO_RSA, 1, pub_root, sec_root, + rc = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, sec_root, get_parameter_u32 (para, pKEYEXPIRE), para); if (!rc) { @@ -2523,7 +2524,7 @@ do_generate_keypair( struct para_data_s *para, } else { - rc = gen_card_key (PUBKEY_ALGO_RSA, 2, pub_root, sec_root, + rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root, get_parameter_u32 (para, pKEYEXPIRE), para); } @@ -2538,7 +2539,7 @@ do_generate_keypair( struct para_data_s *para, if (card && get_parameter (para, pAUTHKEYTYPE)) { - rc = gen_card_key (PUBKEY_ALGO_RSA, 3, pub_root, sec_root, + rc = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, sec_root, get_parameter_u32 (para, pKEYEXPIRE), para); if (!rc) @@ -2768,6 +2769,120 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) return okay; } + +#ifdef ENABLE_CARD_SUPPORT +/* Generate a subkey on a card. */ +int +generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock, + int keyno, const char *serialno) +{ + int okay=0, rc=0; + KBNODE node; + PKT_secret_key *pri_sk = NULL; + int algo; + unsigned int use; + u32 expire; + char *passphrase = NULL; + u32 cur_time; + struct para_data_s *para = NULL; + + assert (keyno >= 1 && keyno <= 3); + + para = xcalloc (1, sizeof *para + strlen (serialno) ); + para->key = pSERIALNO; + strcpy (para->u.value, serialno); + + /* Break out the primary secret key */ + node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); + if(!node) + { + log_error("Oops; secret key not found anymore!\n"); + goto leave; + } + + /* Make a copy of the sk to keep the protected one in the keyblock */ + pri_sk = copy_secret_key (NULL, node->pkt->pkt.secret_key); + + cur_time = make_timestamp(); + if (pri_sk->timestamp > cur_time) + { + ulong d = pri_sk->timestamp - cur_time; + log_info (d==1 ? _("key has been created %lu second " + "in future (time warp or clock problem)\n") + : _("key has been created %lu seconds " + "in future (time warp or clock problem)\n"), d ); + if (!opt.ignore_time_conflict) + { + rc = G10ERR_TIME_CONFLICT; + goto leave; + } + } + + if (pri_sk->version < 4) + { + log_info (_("NOTE: creating subkeys for v3 keys " + "is not OpenPGP compliant\n")); + goto leave; + } + + /* Unprotect to get the passphrase. */ + switch( is_secret_key_protected (pri_sk) ) + { + case -1: + rc = G10ERR_PUBKEY_ALGO; + break; + case 0: + tty_printf("This key is not protected.\n"); + break; + default: + tty_printf("Key is protected.\n"); + rc = check_secret_key( pri_sk, 0 ); + if (!rc) + passphrase = get_last_passphrase(); + break; + } + if (rc) + goto leave; + + algo = PUBKEY_ALGO_RSA; + expire = ask_expire_interval (0); + if (keyno == 1) + use = PUBKEY_USAGE_SIG; + else if (keyno == 2) + use = PUBKEY_USAGE_ENC; + else + use = PUBKEY_USAGE_AUTH; + if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", + _("Really create? ") ) ) + goto leave; + + if (passphrase) + set_next_passphrase (passphrase); + rc = gen_card_key (algo, keyno, 0, pub_keyblock, sec_keyblock, expire, para); + if (!rc) + rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, NULL, use); + if (!rc) + rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, NULL, use); + if (!rc) + { + okay = 1; + write_status_text (STATUS_KEY_CREATED, "S"); + } + + leave: + if (rc) + log_error (_("Key generation failed: %s\n"), g10_errstr(rc) ); + m_free (passphrase); + /* Release the copy of the (now unprotected) secret keys. */ + if (pri_sk) + free_secret_key (pri_sk); + set_next_passphrase( NULL ); + release_parameter_list (para); + return okay; +} +#endif /* !ENABLE_CARD_SUPPORT */ + + /**************** * Write a keyblock to an output stream */ @@ -2787,7 +2902,8 @@ write_keyblock( IOBUF out, KBNODE node ) static int -gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, +gen_card_key (int algo, int keyno, int is_primary, + KBNODE pub_root, KBNODE sec_root, u32 expireval, struct para_data_s *para) { #ifdef ENABLE_CARD_SUPPORT @@ -2848,12 +2964,12 @@ gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, } pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = keyno == 1 ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; + pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); pkt = xcalloc (1,sizeof *pkt); - pkt->pkttype = keyno == 1 ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; + pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); diff --git a/g10/keylist.c b/g10/keylist.c index ce66a1359..cb38026e6 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -41,6 +41,7 @@ static void list_all(int); static void list_one( STRLIST names, int secret); +static void print_card_serialno (PKT_secret_key *sk); struct sig_stats { @@ -752,6 +753,7 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) if( !any ) { if( fpr ) print_fingerprint( pk, sk, 0 ); + print_card_serialno (sk); if( opt.with_key_data ) print_key_data( pk ); any = 1; @@ -805,6 +807,7 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) putchar('\n'); if( fpr ) print_fingerprint( pk, sk, 0 ); /* of the main key */ + print_card_serialno (sk); any = 1; } @@ -817,7 +820,10 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) printf(_(" [expires: %s]"), expirestr_from_sk( sk2 ) ); putchar('\n'); if( fpr > 1 ) - print_fingerprint( NULL, sk2, 0 ); + { + print_fingerprint( NULL, sk2, 0 ); + print_card_serialno (sk2); + } } else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE @@ -861,6 +867,7 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) putchar('\n'); if( fpr ) print_fingerprint( pk, sk, 0 ); + print_card_serialno (sk); any=1; } @@ -1475,6 +1482,41 @@ print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode ) tty_printf ("\n"); } +/* Print the serial number of an OpenPGP card if available. */ +static void +print_card_serialno (PKT_secret_key *sk) +{ + int i; + + if (!sk) + return; + if (!sk->is_protected || sk->protect.s2k.mode != 1002) + return; /* Not a card. */ + if (opt.with_colons) + return; /* Handled elesewhere. */ + + fputs (_(" Card serial no. ="), stdout); + putchar (' '); + if (sk->protect.ivlen == 16 + && !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01", 6) ) + { /* This is an OpenPGP card. Just print the relevant part. */ + for (i=8; i < 14; i++) + { + if (i == 10) + putchar (' '); + printf ("%02X", sk->protect.iv[i]); + } + } + else + { /* Something is wrong: Print all. */ + for (i=0; i < sk->protect.ivlen; i++) + printf ("%02X", sk->protect.iv[i]); + } + putchar ('\n'); +} + + + void set_attrib_fd(int fd) { static int last_fd=-1; diff --git a/g10/main.h b/g10/main.h index d86931e8c..c84a059e3 100644 --- a/g10/main.h +++ b/g10/main.h @@ -162,6 +162,10 @@ int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); +#ifdef ENABLE_CARD_SUPPORT +int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock, + int keyno, const char *serialno); +#endif /*-- openfile.c --*/ int overwrite_filep( const char *fname ); @@ -257,6 +261,7 @@ void unblock_all_signals(void); void change_pin (int no); void card_status (FILE *fp, char *serialno, size_t serialnobuflen); void card_edit (STRLIST commands); +int card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock); #endif #endif /*G10_MAIN_H*/