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-Tag: 2019-10-25~97^2~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a8d26a03c99628ea08efbd01f0a946ed7deda3b9;p=neomutt 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. Co-authored-by: Richard Russon --- diff --git a/autocrypt/autocrypt.c b/autocrypt/autocrypt.c index 3719babbd..79e6cf8a8 100644 --- a/autocrypt/autocrypt.c +++ b/autocrypt/autocrypt.c @@ -199,7 +199,7 @@ int mutt_autocrypt_account_init(bool 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: @@ -475,7 +475,8 @@ int mutt_autocrypt_process_gossip_header(struct Email *e, struct Envelope *prot_ /* This is slightly different from the autocrypt 1.1 spec. * Avoid setting an empty peer.gossip_keydata with a value that matches * the current peer.keydata. */ - if ((peer->gossip_keydata && (mutt_str_strcmp(peer->gossip_keydata, ac_hdr->keydata) != 0)) || + if ((peer->gossip_keydata && + (mutt_str_strcmp(peer->gossip_keydata, ac_hdr->keydata) != 0)) || (!peer->gossip_keydata && (mutt_str_strcmp(peer->keydata, ac_hdr->keydata) != 0))) { import_gpg = true; diff --git a/autocrypt/autocrypt_gpgme.c b/autocrypt/autocrypt_gpgme.c index 3880b2024..87d00daa7 100644 --- a/autocrypt/autocrypt_gpgme.c +++ b/autocrypt/autocrypt_gpgme.c @@ -26,8 +26,11 @@ #include #include "mutt/mutt.h" #include "address/lib.h" +#include "config/lib.h" +#include "curs_lib.h" #include "globals.h" #include "ncrypt/crypt_gpgme.h" +#include "options.h" /** * create_gpgme_context - Create a GPGME context @@ -208,6 +211,102 @@ cleanup: return rc; } +/** + * mutt_autocrypt_gpgme_select_key - Select a Autocrypt key + * @param[in] keyid Key id to select + * @param[out] keydata Buffer for resulting Key data + * @retval 0 Success + * @retval -1 Error + */ +int mutt_autocrypt_gpgme_select_key(struct Buffer *keyid, struct Buffer *keydata) +{ + int rc = -1; + gpgme_ctx_t ctx = NULL; + gpgme_key_t key = NULL; + + OptAutocryptGpgme = true; + 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)); + goto cleanup; + } + + if (export_keydata(ctx, key, keydata)) + goto cleanup; + + rc = 0; + +cleanup: + OptAutocryptGpgme = false; + gpgme_key_unref(key); + gpgme_release(ctx); + return rc; +} + +/** + * mutt_autocrypt_gpgme_select_or_create_key - Ask the user to select or create an Autocrypt key + * @param addr Email Address + * @param keyid Key id + * @param keydata Key data + * @retval 0 Success + * @retval -1 Error + */ +int mutt_autocrypt_gpgme_select_or_create_key(struct Address *addr, struct Buffer *keyid, + struct Buffer *keydata) +{ + int rc = -1; + + /* 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. + */ + const char *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. + */ + const char *letters = _("cs"); + + int choice = mutt_multi_choice(prompt, letters); + switch (choice) + { + case 2: /* select existing */ + rc = mutt_autocrypt_gpgme_select_key(keyid, keydata); + if (rc == 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; + /* fallthrough */ + + case 1: /* create new */ + rc = mutt_autocrypt_gpgme_create_key(addr, keyid, keydata); + } + + return rc; +} + /** * mutt_autocrypt_gpgme_import_key - Read a key from GPGME * @param keydata Buffer for key data diff --git a/autocrypt/autocrypt_private.h b/autocrypt/autocrypt_private.h index 35690370a..7d6d12aa2 100644 --- a/autocrypt/autocrypt_private.h +++ b/autocrypt/autocrypt_private.h @@ -69,5 +69,7 @@ int mutt_autocrypt_gpgme_create_key(struct Address *a int mutt_autocrypt_gpgme_import_key(const char *keydata, struct Buffer *keyid); int mutt_autocrypt_gpgme_init(void); bool mutt_autocrypt_gpgme_is_valid_key(const char *keyid); +int mutt_autocrypt_gpgme_select_key(struct Buffer *keyid, struct Buffer *keydata); +int mutt_autocrypt_gpgme_select_or_create_key(struct Address *addr, struct Buffer *keyid, struct Buffer *keydata); #endif /* MUTT_AUTOCRYPT_AUTOCRYPT_PRIVATE_H */ diff --git a/ncrypt/crypt_gpgme.c b/ncrypt/crypt_gpgme.c index 5afb36860..177ddaa6a 100644 --- a/ncrypt/crypt_gpgme.c +++ b/ncrypt/crypt_gpgme.c @@ -5305,6 +5305,97 @@ char *smime_gpgme_find_keys(struct AddressList *addrlist, bool oppenc_mode) return find_keys(addrlist, APPLICATION_SMIME, oppenc_mode); } +/** + * mutt_gpgme_select_secret_key - Select a private Autocrypt key for a new account + * @param keyid Autocrypt Key id + * @retval 0 Success + * @retval -1 Error + * + * Unfortunately, the internal ncrypt/crypt_gpgme.c functions use CryptKeyInfo, + * 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(struct Buffer *keyid) +{ + int rc = -1, junk; + gpgme_error_t err; + gpgme_key_t key; + gpgme_user_id_t uid; + struct CryptKeyInfo *results = NULL, *k = NULL; + struct CryptKeyInfo **kend = NULL; + struct CryptKeyInfo *choice = NULL; + + gpgme_ctx_t ctx = create_gpgme_context(false); + + /* list all secret keys */ + if (gpgme_op_keylist_start(ctx, NULL, 1)) + goto cleanup; + + kend = &results; + + while (!(err = gpgme_op_keylist_next(ctx, &key))) + { + KeyFlags flags = KEYFLAG_NO_FLAGS; + + 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; + + int idx; + for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) + { + k = mutt_mem_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")); + 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); + + rc = 0; + +cleanup: + crypt_free_key(&choice); + crypt_free_key(&results); + gpgme_release(ctx); + return rc; +} + /** * pgp_gpgme_make_key_attachment - Implements CryptModuleSpecs::pgp_make_key_attachment() */ diff --git a/ncrypt/crypt_gpgme.h b/ncrypt/crypt_gpgme.h index 3d3306238..555b87ee0 100644 --- a/ncrypt/crypt_gpgme.h +++ b/ncrypt/crypt_gpgme.h @@ -29,6 +29,7 @@ struct AddressList; struct Body; +struct Buffer; struct Email; struct Mailbox; struct State; @@ -59,5 +60,6 @@ int smime_gpgme_verify_one(struct Body *sigbdy, struct State *s, const int smime_gpgme_verify_sender(struct Mailbox *m, struct Email *e); const char *mutt_gpgme_print_version(void); +int mutt_gpgme_select_secret_key (struct Buffer *keyid); #endif /* MUTT_NCRYPT_CRYPT_GPGME_H */