From: Kevin McCarthy Date: Wed, 7 Aug 2019 21:12:43 +0000 (-0700) Subject: Add ability to create autocrypt account from an existing key. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d55b0390971e3fa977bc15f390bcdadc80d86532;p=mutt Add ability to create autocrypt account from an existing key. This is useful when adding accounts, or for users who want to use an existing key from the keyring during initial account creation. --- diff --git a/autocrypt/autocrypt.c b/autocrypt/autocrypt.c index 7e139870..4ef47a8d 100644 --- a/autocrypt/autocrypt.c +++ b/autocrypt/autocrypt.c @@ -171,7 +171,7 @@ int mutt_autocrypt_account_init (int prompt) goto cleanup; } - if (mutt_autocrypt_gpgme_create_key (addr, keyid, keydata)) + if (mutt_autocrypt_gpgme_select_or_create_key (addr, keyid, keydata)) goto cleanup; /* L10N: diff --git a/autocrypt/autocrypt_gpgme.c b/autocrypt/autocrypt_gpgme.c index 64619168..da540166 100644 --- a/autocrypt/autocrypt_gpgme.c +++ b/autocrypt/autocrypt_gpgme.c @@ -185,6 +185,91 @@ cleanup: return rv; } +int mutt_autocrypt_gpgme_select_key (BUFFER *keyid, BUFFER *keydata) +{ + int rv = -1; + gpgme_ctx_t ctx = NULL; + gpgme_key_t key = NULL; + + set_option (OPTAUTOCRYPTGPGME); + if (mutt_gpgme_select_secret_key (keyid)) + goto cleanup; + + if (create_gpgme_context (&ctx)) + goto cleanup; + + if (gpgme_get_key (ctx, mutt_b2s (keyid), &key, 0)) + goto cleanup; + + if (key->revoked || key->expired || key->disabled || key->invalid || + !key->can_encrypt || !key->can_sign) + { + /* L10N: + After selecting a key for an autocrypt account, + this is displayed if the key was revoked/expired/disabled/invalid + or can't be used for both signing and encryption. + %s is the key fingerprint. + */ + mutt_error (_("The key %s is not usable for autocrypt"), mutt_b2s (keyid)); + mutt_sleep (1); + goto cleanup; + } + + if (export_keydata (ctx, key, keydata)) + goto cleanup; + + rv = 0; + +cleanup: + unset_option (OPTAUTOCRYPTGPGME); + gpgme_key_unref (key); + gpgme_release (ctx); + return rv; +} + +int mutt_autocrypt_gpgme_select_or_create_key (ADDRESS *addr, BUFFER *keyid, BUFFER *keydata) +{ + int rv = -1; + char *prompt, *letters; + int choice; + + /* L10N: + During autocrypt account creation, this prompt asks the + user whether they want to create a new GPG key for the account, + or select an existing account from the keyring. + */ + prompt = _("(c)reate new, or (s)elect existing GPG key? "); + /* L10N: + The letters corresponding to the + "(c)reate new, or (s)elect existing GPG key?" prompt. + */ + letters = _("cs"); + + choice = mutt_multi_choice (prompt, letters); + switch (choice) + { + case 2: /* select existing */ + rv = mutt_autocrypt_gpgme_select_key (keyid, keydata); + if (rv == 0) + break; + + /* L10N: + During autocrypt account creation, if selecting an existing key fails + for some reason, we prompt to see if they want to create a key instead. + */ + if (mutt_yesorno (_("Create a new gpg key for this account, instead?"), + MUTT_YES) == MUTT_NO) + break; + + /* otherwise fall through to create new key */ + + case 1: /* create new */ + rv = mutt_autocrypt_gpgme_create_key (addr, keyid, keydata); + } + + return rv; +} + int mutt_autocrypt_gpgme_import_key (const char *keydata, BUFFER *keyid) { int rv = -1; diff --git a/autocrypt/autocrypt_private.h b/autocrypt/autocrypt_private.h index 43b6b29d..37b4f311 100644 --- a/autocrypt/autocrypt_private.h +++ b/autocrypt/autocrypt_private.h @@ -55,7 +55,9 @@ int mutt_autocrypt_schema_init (void); int mutt_autocrypt_schema_update (void); int mutt_autocrypt_gpgme_init (void); +int mutt_autocrypt_gpgme_select_or_create_key (ADDRESS *addr, BUFFER *keyid, BUFFER *keydata); int mutt_autocrypt_gpgme_create_key (ADDRESS *addr, BUFFER *keyid, BUFFER *keydata); +int mutt_autocrypt_gpgme_select_key (BUFFER *keyid, BUFFER *keydata); int mutt_autocrypt_gpgme_import_key (const char *keydata, BUFFER *keyid); int mutt_autocrypt_gpgme_is_valid_key (const char *keyid); diff --git a/crypt-gpgme.c b/crypt-gpgme.c index 418bc180..28550172 100644 --- a/crypt-gpgme.c +++ b/crypt-gpgme.c @@ -5010,6 +5010,97 @@ char *smime_gpgme_findkeys (ADDRESS *adrlist, int oppenc_mode) return find_keys (adrlist, APPLICATION_SMIME, oppenc_mode); } +/* + * This function is used by autocrypt to select a private key for + * a new account. + * + * Unfortunately, the internal crypt-gpgme.c functions use crypt_key_t, + * and so aren't exportable. + * + * This function queries all private keys, provides the crypt_select_keys() + * menu, and returns the selected key fingerprint in keyid. + */ +int mutt_gpgme_select_secret_key (BUFFER *keyid) +{ + int rv = -1, junk; + gpgme_ctx_t ctx = NULL; + gpgme_error_t err; + gpgme_key_t key; + gpgme_user_id_t uid; + crypt_key_t *results = NULL, *k, **kend; + crypt_key_t *choice = NULL; + unsigned int flags, idx; + + ctx = create_gpgme_context (0); + + /* list all secret keys */ + if (gpgme_op_keylist_start (ctx, NULL, 1)) + goto cleanup; + + kend = &results; + + while (!(err = gpgme_op_keylist_next (ctx, &key)) ) + { + flags = 0; + + if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) + flags |= KEYFLAG_CANENCRYPT; + if (key_check_cap (key, KEY_CAP_CAN_SIGN)) + flags |= KEYFLAG_CANSIGN; + + if (key->revoked) + flags |= KEYFLAG_REVOKED; + if (key->expired) + flags |= KEYFLAG_EXPIRED; + if (key->disabled) + flags |= KEYFLAG_DISABLED; + + for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) + { + k = safe_calloc (1, sizeof *k); + k->kobj = key; + gpgme_key_ref (k->kobj); + k->idx = idx; + k->uid = uid->uid; + k->flags = flags; + if (uid->revoked) + k->flags |= KEYFLAG_REVOKED; + k->validity = uid->validity; + *kend = k; + kend = &k->next; + } + gpgme_key_unref (key); + } + if (gpg_err_code (err) != GPG_ERR_EOF) + mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err)); + gpgme_op_keylist_end (ctx); + + if (!results) + { + /* L10N: + mutt_gpgme_select_secret_key() tries to list all secret keys to choose + from. This error is displayed if no results were found. + */ + mutt_error (_("No secret keys found")); + mutt_sleep (1); + goto cleanup; + } + + choice = crypt_select_key (results, NULL, "*", APPLICATION_PGP, &junk); + if (!(choice && choice->kobj && choice->kobj->subkeys && + choice->kobj->subkeys->fpr)) + goto cleanup; + mutt_buffer_strcpy (keyid, choice->kobj->subkeys->fpr); + + rv = 0; + +cleanup: + crypt_free_key (&choice); + crypt_free_key (&results); + gpgme_release (ctx); + return rv; +} + BODY *pgp_gpgme_make_key_attachment (char *tempf) { crypt_key_t *key = NULL; diff --git a/crypt-gpgme.h b/crypt-gpgme.h index a2e06b86..827d86a8 100644 --- a/crypt-gpgme.h +++ b/crypt-gpgme.h @@ -55,4 +55,7 @@ int smime_gpgme_verify_sender (HEADER *h); void mutt_gpgme_set_sender (const char *sender); +int mutt_gpgme_select_secret_key (BUFFER *keyid); + + #endif