/**************** * Check the secret key * Ask up to 3 (or n) times for a correct passphrase * If n is negative, disable the key info prompt and make n=abs(n) */ int check_secret_key( PKT_secret_key *sk, int n ) { int rc = gpg_error (GPG_ERR_BAD_PASSPHRASE); int i,mode; if (sk && sk->is_protected && sk->protect.s2k.mode == 1002) return 0; /* Let the scdaemon handle this. */ if(n<0) { n=abs(n); mode=1; } else mode=0; if( n < 1 ) n = 3; /* Use the default value */ for(i=0; i < n && gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE; i++ ) { int canceled = 0; const char *tryagain = NULL; if (i) { tryagain = N_("Invalid passphrase; please try again"); log_info (_("%s ...\n"), _(tryagain)); } rc = do_check( sk, tryagain, mode, &canceled ); if ( gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE && is_status_enabled () ) { u32 kid[2]; char buf[50]; keyid_from_sk( sk, kid ); sprintf(buf, "%08lX%08lX", (ulong)kid[0], (ulong)kid[1]); write_status_text( STATUS_BAD_PASSPHRASE, buf ); } if( have_static_passphrase() || canceled) break; } if( !rc ) write_status( STATUS_GOOD_PASSPHRASE ); return rc; }
/* Return a new DEK object using the string-to-key specifier 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 NULL, sets it to true. MODE 0: Allow cached passphrase 1: Ignore cached passphrase 2: Ditto, but create a new key 3: Allow cached passphrase; use the S2K salt as the cache ID 4: Ditto, but create a new key */ 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) { char *pw = NULL; DEK *dek; STRING2KEY help_s2k; int dummy_canceled; char s2k_cacheidbuf[1+16+1], *s2k_cacheid = NULL; if (!canceled) canceled = &dummy_canceled; *canceled = 0; if ( !s2k ) { log_assert (mode != 3 && mode != 4); /* This is used for the old rfc1991 mode * Note: This must match the code in encode.c with opt.rfc1991 set */ s2k = &help_s2k; s2k->mode = 0; s2k->hash_algo = S2K_DIGEST_ALGO; } /* Create a new salt or what else to be filled into the s2k for a new key. */ if ((mode == 2 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3)) { gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM); if ( s2k->mode == 3 ) { /* We delay the encoding until it is really needed. This is if we are going to dynamically calibrate it, we need to call out to gpg-agent and that should not be done during option processing in main(). */ if (!opt.s2k_count) opt.s2k_count = encode_s2k_iterations (0); s2k->count = opt.s2k_count; } } /* If we do not have a passphrase available in NEXT_PW and status information are request, we print them now. */ if ( !next_pw && is_status_enabled() ) { char buf[50]; if ( keyid ) { emit_status_need_passphrase (keyid, keyid[2] && keyid[3]? keyid+2:NULL, pubkey_algo); } else { snprintf (buf, sizeof buf -1, "%d %d %d", cipher_algo, s2k->mode, s2k->hash_algo ); write_status_text ( STATUS_NEED_PASSPHRASE_SYM, buf ); } } /* If we do have a keyID, we do not have a passphrase available in NEXT_PW, we are not running in batch mode and we do not want to ignore the passphrase cache (mode!=1), print a prompt with information on that key. */ if ( keyid && !opt.batch && !next_pw && mode!=1 ) { PKT_public_key *pk = xmalloc_clear( sizeof *pk ); char *p; p = get_user_id_native(keyid); tty_printf ("\n"); tty_printf (_("You need a passphrase to unlock the secret key for\n" "user: \"%s\"\n"),p); xfree(p); if ( !get_pubkey( pk, keyid ) ) { const char *s = openpgp_pk_algo_name ( pk->pubkey_algo ); tty_printf (_("%u-bit %s key, ID %s, created %s"), nbits_from_pk( pk ), s?s:"?", keystr(keyid), strtimestamp(pk->timestamp) ); if ( keyid[2] && keyid[3] && keyid[0] != keyid[2] && keyid[1] != keyid[3] ) { if ( keystrlen () > 10 ) { tty_printf ("\n"); tty_printf (_(" (subkey on main key ID %s)"), keystr(&keyid[2]) ); } else tty_printf ( _(" (main key ID %s)"), keystr(&keyid[2]) ); } tty_printf("\n"); } tty_printf("\n"); free_public_key (pk); } if ( next_pw ) { /* Simply return the passphrase we already have in NEXT_PW. */ pw = next_pw; next_pw = NULL; } else if ( have_static_passphrase () ) { /* Return the passphrase we have stored in FD_PASSWD. */ pw = xmalloc_secure ( strlen(fd_passwd)+1 ); strcpy ( pw, fd_passwd ); } else { if ((mode == 3 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3)) { memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf); *s2k_cacheidbuf = 'S'; bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1); s2k_cacheid = s2k_cacheidbuf; } if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK) { char buf[32]; snprintf (buf, sizeof (buf), "%u", 100); write_status_text (STATUS_INQUIRE_MAXLEN, buf); } /* Divert to the gpg-agent. */ pw = passphrase_get (keyid, mode == 2, s2k_cacheid, (mode == 2 || mode == 4)? opt.passphrase_repeat : 0, tryagain_text, custdesc, custprompt, canceled); if (*canceled) { xfree (pw); write_status( STATUS_MISSING_PASSPHRASE ); return NULL; } } if ( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); /* Hash the passphrase and store it in a newly allocated DEK object. Keep a copy of the passphrase in LAST_PW for use by get_last_passphrase(). */ dek = xmalloc_secure_clear ( sizeof *dek ); dek->algo = cipher_algo; if ( (!pw || !*pw) && (mode == 2 || mode == 4)) dek->keylen = 0; else { gpg_error_t err; dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo); if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key))) BUG (); err = gcry_kdf_derive (pw, strlen (pw), s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K : s2k->mode == 1? GCRY_KDF_SALTED_S2K : /* */ GCRY_KDF_SIMPLE_S2K, s2k->hash_algo, s2k->salt, 8, S2K_DECODE_COUNT(s2k->count), dek->keylen, dek->key); if (err) { log_error ("gcry_kdf_derive failed: %s", gpg_strerror (err)); xfree (pw); xfree (dek); write_status( STATUS_MISSING_PASSPHRASE ); return NULL; } } if (s2k_cacheid) memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid); xfree(last_pw); last_pw = pw; return dek; }
/* Return a new DEK object using the string-to-key specifier S2K. * Returns NULL if the user canceled the passphrase entry and if * CANCELED is not NULL, sets it to true. * * If CREATE is true a new passphrase sll be created. If NOCACHE is * true the symmetric key caching will not be used. */ DEK * passphrase_to_dek (int cipher_algo, STRING2KEY *s2k, int create, int nocache, const char *tryagain_text, int *canceled) { char *pw = NULL; DEK *dek; STRING2KEY help_s2k; int dummy_canceled; char s2k_cacheidbuf[1+16+1]; char *s2k_cacheid = NULL; if (!canceled) canceled = &dummy_canceled; *canceled = 0; if ( !s2k ) { log_assert (create && !nocache); /* This is used for the old rfc1991 mode * Note: This must match the code in encode.c with opt.rfc1991 set */ memset (&help_s2k, 0, sizeof (help_s2k)); s2k = &help_s2k; s2k->hash_algo = S2K_DIGEST_ALGO; } /* Create a new salt or what else to be filled into the s2k for a new key. */ if (create && (s2k->mode == 1 || s2k->mode == 3)) { gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM); if ( s2k->mode == 3 ) { /* We delay the encoding until it is really needed. This is if we are going to dynamically calibrate it, we need to call out to gpg-agent and that should not be done during option processing in main(). */ if (!opt.s2k_count) opt.s2k_count = encode_s2k_iterations (0); s2k->count = opt.s2k_count; } } /* If we do not have a passphrase available in NEXT_PW and status information are request, we print them now. */ if ( !next_pw && is_status_enabled() ) { char buf[50]; snprintf (buf, sizeof buf, "%d %d %d", cipher_algo, s2k->mode, s2k->hash_algo ); write_status_text ( STATUS_NEED_PASSPHRASE_SYM, buf ); } if ( next_pw ) { /* Simply return the passphrase we already have in NEXT_PW. */ pw = next_pw; next_pw = NULL; } else if ( have_static_passphrase () ) { /* Return the passphrase we have stored in FD_PASSWD. */ pw = xmalloc_secure ( strlen(fd_passwd)+1 ); strcpy ( pw, fd_passwd ); } else { if (!nocache && (s2k->mode == 1 || s2k->mode == 3)) { memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf); *s2k_cacheidbuf = 'S'; bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1); s2k_cacheid = s2k_cacheidbuf; } if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK) { char buf[32]; snprintf (buf, sizeof (buf), "%u", 100); write_status_text (STATUS_INQUIRE_MAXLEN, buf); } /* Divert to the gpg-agent. */ pw = passphrase_get (create && nocache, s2k_cacheid, create? opt.passphrase_repeat : 0, tryagain_text, canceled); if (*canceled) { xfree (pw); write_status( STATUS_MISSING_PASSPHRASE ); return NULL; } } if ( !pw || !*pw ) write_status( STATUS_MISSING_PASSPHRASE ); /* Hash the passphrase and store it in a newly allocated DEK object. Keep a copy of the passphrase in LAST_PW for use by get_last_passphrase(). */ dek = xmalloc_secure_clear ( sizeof *dek ); dek->algo = cipher_algo; if ( (!pw || !*pw) && create) dek->keylen = 0; else { gpg_error_t err; dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo); if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key))) BUG (); err = gcry_kdf_derive (pw, strlen (pw), s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K : s2k->mode == 1? GCRY_KDF_SALTED_S2K : /* */ GCRY_KDF_SIMPLE_S2K, s2k->hash_algo, s2k->salt, 8, S2K_DECODE_COUNT(s2k->count), dek->keylen, dek->key); if (err) { log_error ("gcry_kdf_derive failed: %s", gpg_strerror (err)); xfree (pw); xfree (dek); write_status( STATUS_MISSING_PASSPHRASE ); return NULL; } } if (s2k_cacheid) memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid); xfree(last_pw); last_pw = pw; return dek; }