From c4e92c334418ebd538ef298f904149c31ac29137 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 15 May 2009 19:26:46 +0000 Subject: [PATCH] Made card key generate with backup key work for 2048 bit. Improved card key generation prompts. --- doc/gpg-agent.texi | 11 +++++-- g10/ChangeLog | 21 +++++++++++++ g10/call-agent.c | 22 ++++++++++++-- g10/call-agent.h | 5 ++++ g10/card-util.c | 16 +++++++++- g10/keydb.h | 10 +++---- g10/keygen.c | 42 ++++++++++++++++++++------ g10/passphrase.c | 75 +++++++++++++--------------------------------- scd/app-openpgp.c | 3 ++ 9 files changed, 130 insertions(+), 75 deletions(-) diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index dad050124..16dd52197 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -579,8 +579,10 @@ manually. Comment lines, indicated by a leading hash mark, as well as empty lines are ignored. An entry starts with optional whitespace, followed by the keygrip of the key given as 40 hex digits, optionally followed by the caching TTL in seconds and another optional field for -arbitrary flags. The keygrip may be prefixed with a @code{!} to -disable this entry. +arbitrary flags. A non-zero TTL overrides the global default as +set by @option{--default-cache-ttl-ssh}. + +The keygrip may be prefixed with a @code{!} to disable an entry entry. The following example lists exactly one key. Note that keys available through a OpenPGP smartcard in the active smartcard reader are @@ -1054,7 +1056,7 @@ special handling of passphrases. This command uses a syntax which helps clients to use the agent with minimum effort. @example - GET_PASSPHRASE [--data] [--check] [--no-ask] @var{cache_id} [@var{error_message} @var{prompt} @var{description}] + GET_PASSPHRASE [--data] [--check] [--no-ask] [--repeat[=N]] [--qualitybar] @var{cache_id} [@var{error_message} @var{prompt} @var{description}] @end example @var{cache_id} is expected to be a string used to identify a cached @@ -1089,6 +1091,9 @@ If the option @option{--no-ask} is used and the passphrase is not in the cache the user will not be asked to enter a passphrase but the error code @code{GPG_ERR_NO_DATA} is returned. +If the option @option{--qualitybar} is used and a minimum passphrase +length has been configured, a visual indication of the entered +passphrase quality is shown. @example CLEAR_PASSPHRASE @var{cache_id} diff --git a/g10/ChangeLog b/g10/ChangeLog index bd54d1976..1f9751293 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,8 +1,29 @@ +2009-05-15 Werner Koch + + * keygen.c (gen_card_key_with_backup): Get the size of the key + from the card. + * call-agent.h (struct agent_card_info_s): Add field KEY_ATTR. + * call-agent.c (learn_status_cb): Support KEY-ATTR. + * card-util.c (card_status): Print key attributes. + 2009-05-15 Marcus Brinkmann * gpg.c (gpgconf_list): Remove dead entry "allow-pka-lookup" (a verify option for a couple of years now). +2009-05-14 Werner Koch + + * call-agent.c (agent_get_passphrase): Add arg CHECK. + * passphrase.c (passphrase_get): Pass new arg. + + * keygen.c (gen_card_key_with_backup): Print a status error. + (do_generate_keypair): Ditto. + (do_ask_passphrase): Add arg MODE. + (generate_raw_key): Call with mode 1. + * passphrase.c (ask_passphrase): Remove becuase it is not used. + (passphrase_to_dek): Factor code out to ... + (passphrase_to_dek_ext): .. New. Add args CUSTDESC and CUSTPROMPT. + 2009-05-13 Werner Koch * keygen.c (parse_expire_string): Base ISO date string at noon. diff --git a/g10/call-agent.c b/g10/call-agent.c index 444048a47..c8a2013ac 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -325,7 +325,19 @@ learn_status_cb (void *opaque, const char *line) else if (no == 3) parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3); } - + else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen)) + { + int keyno, algo, nbits; + + sscanf (line, "%d %d %d", &keyno, &algo, &nbits); + keyno--; + if (keyno >= 0 && keyno < DIM (parm->key_attr)) + { + parm->key_attr[keyno].algo = algo; + parm->key_attr[keyno].nbits = nbits; + } + } + return 0; } @@ -343,6 +355,9 @@ agent_learn (struct agent_card_info_s *info) rc = assuan_transact (agent_ctx, "LEARN --send", dummy_data_cb, NULL, default_inq_cb, NULL, learn_status_cb, info); + /* Also try to get the key attributes. */ + if (!rc) + agent_scd_getattr ("KEY-ATTR", info); return rc; } @@ -535,7 +550,6 @@ scd_genkey_cb (void *opaque, const char *line) int keywordlen; gpg_error_t rc; - log_debug ("got status line `%s'\n", line); for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) @@ -827,6 +841,7 @@ agent_get_passphrase (const char *cache_id, const char *prompt, const char *desc_msg, int repeat, + int check, char **r_passphrase) { int rc; @@ -863,8 +878,9 @@ agent_get_passphrase (const char *cache_id, goto no_mem; snprintf (line, DIM(line)-1, - "GET_PASSPHRASE --data --repeat=%d -- %s %s %s %s", + "GET_PASSPHRASE --data --repeat=%d%s -- %s %s %s %s", repeat, + check? " --check --qualitybar":"", arg1? arg1:"X", arg2? arg2:"X", arg3? arg3:"X", diff --git a/g10/call-agent.h b/g10/call-agent.h index ebe37b1cd..577926e44 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -53,6 +53,10 @@ struct agent_card_info_s 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. */ + struct { /* Array with key attributes. */ + int algo; /* Algorithm identifier. */ + unsigned int nbits; /* Supported keysize. */ + } key_attr[3]; }; struct agent_card_genkey_s { @@ -116,6 +120,7 @@ gpg_error_t agent_get_passphrase (const char *cache_id, const char *prompt, const char *desc_msg, int repeat, + int check, char **r_passphrase); /* Send the CLEAR_PASSPHRASE command to the agent. */ diff --git a/g10/card-util.c b/g10/card-util.c index 1d263669f..7d36ebf9c 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -443,6 +443,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) fputs (":\n", fp); fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached); + for (i=0; i < DIM (info.key_attr); i++) + if (info.key_attr[0].algo) + fprintf (fp, "keyattr:%d:%d:%u:\n", i+1, + info.key_attr[i].algo, info.key_attr[i].nbits); fprintf (fp, "maxpinlen:%d:%d:%d:\n", info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]); fprintf (fp, "pinretry:%d:%d:%d:\n", @@ -518,6 +522,16 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) } tty_fprintf (fp, "Signature PIN ....: %s\n", info.chv1_cached? _("not forced"): _("forced")); + if (info.key_attr[0].algo) + { + tty_fprintf (fp, "Key attributes ...:"); + for (i=0; i < DIM (info.key_attr); i++) + tty_fprintf (fp, " %u%c", + info.key_attr[i].nbits, + info.key_attr[i].algo == 1? 'R': + info.key_attr[i].algo == 17? 'D': '?'); + tty_fprintf (fp, "\n"); + } tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n", info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]); tty_fprintf (fp, "PIN retry counter : %d %d %d\n", @@ -1077,7 +1091,7 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1) *forced_chv1 = !info->chv1_cached; if (*forced_chv1) - { /* Switch of the forced mode so that during key generation we + { /* Switch off 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, info->serialno); diff --git a/g10/keydb.h b/g10/keydb.h index 4e8245876..ca3ca77ec 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -202,11 +202,11 @@ int have_static_passphrase(void); void set_passphrase_from_string(const char *pass); void read_passphrase_from_fd( int fd ); void passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ); -char *ask_passphrase (const char *description, - const char *tryagain_text, - const char *promptid, - const char *prompt, - const char *cacheid, int *canceled); +DEK *passphrase_to_dek_ext(u32 *keyid, int pubkey_algo, + int cipher_algo, STRING2KEY *s2k, int mode, + const char *tryagain_text, + const char *custdesc, const char *custprompt, + int *canceled); DEK *passphrase_to_dek( u32 *keyid, int pubkey_algo, int cipher_algo, STRING2KEY *s2k, int mode, const char *tryagain_text, int *canceled); diff --git a/g10/keygen.c b/g10/keygen.c index 8afa74e3e..96c68334e 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -2153,21 +2153,28 @@ ask_user_id( int mode ) } +/* MODE 0 - standard + 1 - Ask for passphrase of the card backup key. */ static DEK * -do_ask_passphrase ( STRING2KEY **ret_s2k, int *r_canceled ) +do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled) { DEK *dek = NULL; STRING2KEY *s2k; const char *errtext = NULL; + const char *custdesc = NULL; tty_printf(_("You need a Passphrase to protect your secret key.\n\n") ); + if (mode == 1) + custdesc = _("Please enter a passphrase to protect the off-card " + "backup of the new encryption key."); + s2k = xmalloc_secure( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; - dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k,2, - errtext, r_canceled); + dek = passphrase_to_dek_ext (NULL, 0, opt.s2k_cipher_algo, s2k, 2, + errtext, custdesc, NULL, r_canceled); if (!dek && *r_canceled) { xfree(dek); dek = NULL; xfree(s2k); s2k = NULL; @@ -2587,7 +2594,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname, STRING2KEY *s2k; DEK *dek; - dek = do_ask_passphrase ( &s2k, &canceled ); + dek = do_ask_passphrase (&s2k, 0, &canceled); if (dek) { r = xmalloc_clear( sizeof *r ); @@ -3085,7 +3092,7 @@ generate_keypair (const char *fname, const char *card_serialno, para = r; canceled = 0; - dek = card_serialno? NULL : do_ask_passphrase ( &s2k, &canceled ); + dek = card_serialno? NULL : do_ask_passphrase (&s2k, 0, &canceled); if( dek ) { r = xmalloc_clear( sizeof *r ); @@ -3143,7 +3150,7 @@ generate_raw_key (int algo, unsigned int nbits, u32 created_at, log_info(_("keysize rounded up to %u bits\n"), nbits ); } - dek = do_ask_passphrase (&s2k, &canceled); + dek = do_ask_passphrase (&s2k, 1, &canceled); if (canceled) { rc = gpg_error (GPG_ERR_CANCELED); @@ -3547,6 +3554,7 @@ do_generate_keypair (struct para_data_s *para, log_error ("key generation failed: %s\n", g10_errstr(rc) ); else tty_printf (_("Key generation failed: %s\n"), g10_errstr(rc) ); + write_status_error (card? "card_key_generate":"key_generate", rc); print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); } else @@ -3660,7 +3668,7 @@ generate_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock) canceled = 0; if (ask_pass) - dek = do_ask_passphrase (&s2k, &canceled); + dek = do_ask_passphrase (&s2k, 0, &canceled); else if (passphrase) { s2k = xmalloc_secure ( sizeof *s2k ); @@ -3951,19 +3959,35 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary, PKT_public_key *pk; size_t n; int i; + unsigned int nbits; + + /* Get the size of the key directly from the card. */ + { + struct agent_card_info_s info; + + memset (&info, 0, sizeof info); + if (!agent_scd_getattr ("KEY-ATTR", &info) + && info.key_attr[1].algo) + nbits = info.key_attr[1].nbits; + else + nbits = 1024; /* All pre-v2.0 cards. */ + agent_release_card_info (&info); + } - rc = generate_raw_key (algo, 1024, timestamp, + /* Create a key of this size in memory. */ + rc = generate_raw_key (algo, nbits, timestamp, &sk_unprotected, &sk_protected); if (rc) return rc; - /* First, store the key to the card. */ + /* Store the key to the card. */ rc = save_unprotected_key_to_card (sk_unprotected, keyno); if (rc) { log_error (_("storing key onto card failed: %s\n"), g10_errstr (rc)); free_secret_key (sk_unprotected); free_secret_key (sk_protected); + write_status_error ("save_key_to_card", rc); return rc; } diff --git a/g10/passphrase.c b/g10/passphrase.c index 8b952f72a..f5d301398 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -236,7 +236,8 @@ read_passphrase_from_fd( int fd ) /* * Ask the GPG Agent for the passphrase. * Mode 0: Allow cached passphrase - * 1: No cached passphrase FIXME: Not really implemented + * 1: No cached passphrase; that is we are asking for a new passphrase + * FIXME: Only partially implemented * * Note that TRYAGAIN_TEXT must not be translated. If CANCELED is not * NULL, the function does set it to 1 if the user canceled the @@ -260,6 +261,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, char *my_prompt; char hexfprbuf[20*2+1]; const char *my_cacheid; + int check = (mode == 1); if (canceled) *canceled = 0; @@ -347,7 +349,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, my_prompt = custom_prompt ? native_to_utf8 (custom_prompt): NULL; rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext, - repeat, &pw); + repeat, check, &pw); xfree (my_prompt); xfree (atext); atext = NULL; @@ -432,54 +434,6 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo ) } -/**************** - * Ask for a passphrase and return that string. - */ -char * -ask_passphrase (const char *description, - const char *tryagain_text, - const char *promptid, - const char *prompt, - const char *cacheid, int *canceled) -{ - char *pw = NULL; - - (void)promptid; - - if (canceled) - *canceled = 0; - - if (!opt.batch && description) - { - if (strchr (description, '%')) - { - char *tmp = percent_plus_unescape (description, 0xff); - if (!tmp) - log_fatal(_("out of core\n")); - tty_printf ("\n%s\n", tmp); - xfree (tmp); - } - else - tty_printf ("\n%s\n",description); - } - - if (have_static_passphrase ()) - { - pw = xmalloc_secure (strlen(fd_passwd)+1); - strcpy (pw, fd_passwd); - } - else - pw = passphrase_get (NULL, 0, cacheid, 0, - tryagain_text, description, prompt, - canceled ); - - if (!pw || !*pw) - write_status( STATUS_MISSING_PASSPHRASE ); - - return pw; -} - - /* Return a new DEK object Using the string-to-key sepcifier S2K. Use KEYID and PUBKEY_ALGO to prompt the user. Returns NULL is the user selected to cancel the passphrase entry and if CANCELED is not @@ -490,9 +444,11 @@ ask_passphrase (const char *description, 2: Ditto, but change the text to "repeat entry" */ DEK * -passphrase_to_dek (u32 *keyid, int pubkey_algo, - int cipher_algo, STRING2KEY *s2k, int mode, - const char *tryagain_text, int *canceled) +passphrase_to_dek_ext (u32 *keyid, int pubkey_algo, + int cipher_algo, STRING2KEY *s2k, int mode, + const char *tryagain_text, + const char *custdesc, const char *custprompt, + int *canceled) { char *pw = NULL; DEK *dek; @@ -612,7 +568,7 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo, /* Divert to the gpg-agent. */ pw = passphrase_get ( keyid, mode == 2, NULL, mode == 2? opt.passwd_repeat: 0, - tryagain_text, NULL, NULL, canceled ); + tryagain_text, custdesc, custprompt, canceled); if (*canceled) { xfree (pw); @@ -637,3 +593,14 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo, last_pw = pw; return dek; } + + +DEK * +passphrase_to_dek (u32 *keyid, int pubkey_algo, + int cipher_algo, STRING2KEY *s2k, int mode, + const char *tryagain_text, int *canceled) +{ + return passphrase_to_dek_ext (keyid, pubkey_algo, cipher_algo, + s2k, mode, tryagain_text, NULL, NULL, + canceled); +} diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index a633feab6..02c1d338f 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -2368,6 +2368,9 @@ do_writekey (app_t app, ctrl_t ctrl, maxbits = app->app_local->keyattr[keyno].n_bits; nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0; + if (opt.verbose) + log_info ("RSA modulus size is %u bits (%u bytes)\n", + nbits, (unsigned int)rsa_n_len); if (nbits != maxbits) { log_error (_("RSA modulus missing or not of size %d bits\n"),