]> granicus.if.org Git - mutt/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)
committerKevin McCarthy <kevin@8t8.us>
Wed, 7 Aug 2019 21:12:43 +0000 (14:12 -0700)
This is useful when adding accounts, or for users who want to use an
existing key from the keyring during initial account creation.

autocrypt/autocrypt.c
autocrypt/autocrypt_gpgme.c
autocrypt/autocrypt_private.h
crypt-gpgme.c
crypt-gpgme.h

index 7e139870a6716b688adca70fc8f4743568957aa7..4ef47a8db8645949f24be3de7cc4bdc757de6dd6 100644 (file)
@@ -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:
index 64619168800eb2fefefd1316b66d1d149b93d8c9..da540166f85b69b3e8fbcdeff3b46c4b0c446615 100644 (file)
@@ -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;
index 43b6b29da651de55ed33550252c703939a324556..37b4f311c3290640e4d5fe4577074793ac27f19a 100644 (file)
@@ -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);
 
index 418bc180215740bebd5bdfe19d2fc2f5c57582b7..28550172b64c2209eb88fdb7d6cdd6739650a71c 100644 (file)
@@ -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;
index a2e06b865b3ba34a55433e29a8cb389e3b638276..827d86a82b95d9bd55951c5ee6f7d26a7e3785e1 100644 (file)
@@ -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