]> granicus.if.org Git - neomutt/commitdiff
Add ability to create autocrypt account from an existing key
authorKevin McCarthy <kevin@8t8.us>
Wed, 7 Aug 2019 21:12:43 +0000 (14:12 -0700)
committerRichard Russon <rich@flatcap.org>
Mon, 19 Aug 2019 23:14:28 +0000 (00:14 +0100)
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 <rich@flatcap.org>
autocrypt/autocrypt.c
autocrypt/autocrypt_gpgme.c
autocrypt/autocrypt_private.h
ncrypt/crypt_gpgme.c
ncrypt/crypt_gpgme.h

index 3719babbde64112f2c386c535d82a332e647afbd..79e6cf8a86e6472e8b924001dc4f13bcd41688f4 100644 (file)
@@ -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;
index 3880b20242f4e128e26e0ffb587f57c471eaad40..87d00daa7378518083c0de854e27c56297f340cd 100644 (file)
 #include <stdbool.h>
 #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
index 35690370ab18714223e0341a1dc2cb32133cef7c..7d6d12aa2f933a4a24a14866799cc5504200e307 100644 (file)
@@ -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 */
index 5afb36860d9810c81ddcd4e06f92d5c8bae5ac16..177ddaa6ace3ee5719e8ded1d2e7877c3aea0fb7 100644 (file)
@@ -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()
  */
index 3d3306238a0d0b9f8920f29e615991488fb9cb78..555b87ee01505feb0c7bb40195217e55dd6fef8e 100644 (file)
@@ -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 */