1 /**
2  * @file
3  * Autocrypt GPGME handler
4  *
5  * @authors
6  * Copyright (C) 2019 Kevin J. McCarthy <kevin@8t8.us>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /**
24  * @page autocrypt_gpgme Autocrypt GPGME handler
25  *
26  * Autocrypt GPGME handler
27  */
28 
29 #include "config.h"
30 #include <stddef.h>
31 #include <gpgme.h>
32 #include <stdbool.h>
33 #include "private.h"
34 #include "mutt/lib.h"
35 #include "address/lib.h"
36 #include "config/lib.h"
37 #include "core/lib.h"
38 #include "ncrypt/lib.h"
39 #include "question/lib.h"
40 #include "options.h"
41 
42 /**
43  * create_gpgme_context - Create a GPGME context
44  * @param ctx GPGME context to initialise
45  * @retval  0 Success
46  * @retval -1 Error
47  */
create_gpgme_context(gpgme_ctx_t * ctx)48 static int create_gpgme_context(gpgme_ctx_t *ctx)
49 {
50   const char *const c_autocrypt_dir =
51       cs_subset_path(NeoMutt->sub, "autocrypt_dir");
52   gpgme_error_t err = gpgme_new(ctx);
53   if (!err)
54     err = gpgme_ctx_set_engine_info(*ctx, GPGME_PROTOCOL_OpenPGP, NULL, c_autocrypt_dir);
55   if (err)
56   {
57     mutt_error(_("error creating GPGME context: %s"), gpgme_strerror(err));
58     return -1;
59   }
60 
61   return 0;
62 }
63 
64 /**
65  * mutt_autocrypt_gpgme_init - Initialise GPGME
66  */
mutt_autocrypt_gpgme_init(void)67 int mutt_autocrypt_gpgme_init(void)
68 {
69   pgp_gpgme_init();
70   return 0;
71 }
72 
73 /**
74  * export_keydata - Export Key data from GPGME into a Buffer
75  * @param ctx     GPGME context
76  * @param key     GPGME key
77  * @param keydata Buffer for results
78  * @retval  0 Success
79  * @retval -1 Error
80  */
export_keydata(gpgme_ctx_t ctx,gpgme_key_t key,struct Buffer * keydata)81 static int export_keydata(gpgme_ctx_t ctx, gpgme_key_t key, struct Buffer *keydata)
82 {
83   int rc = -1;
84   gpgme_data_t dh = NULL;
85   gpgme_key_t export_keys[2] = { 0 };
86   size_t export_data_len;
87 
88   if (gpgme_data_new(&dh))
89     goto cleanup;
90 
91     /* This doesn't seem to work */
92 #if 0
93   if (gpgme_data_set_encoding (dh, GPGME_DATA_ENCODING_BASE64))
94     goto cleanup;
95 #endif
96 
97   export_keys[0] = key;
98   export_keys[1] = NULL;
99   if (gpgme_op_export_keys(ctx, export_keys, GPGME_EXPORT_MODE_MINIMAL, dh))
100     goto cleanup;
101 
102   char *export_data = gpgme_data_release_and_get_mem(dh, &export_data_len);
103   dh = NULL;
104 
105   mutt_b64_buffer_encode(keydata, export_data, export_data_len);
106   gpgme_free(export_data);
107   export_data = NULL;
108 
109   rc = 0;
110 
111 cleanup:
112   gpgme_data_release(dh);
113   return rc;
114 }
115 
116 #if 0
117 /**
118  * mutt_autocrypt_gpgme_export_key - Export a GPGME key
119  * @param keyid   GPGME Key id
120  * @param keydata Buffer for results
121  * @retval  0 Success
122  * @retval -1 Error
123  */
124 int mutt_autocrypt_gpgme_export_key(const char *keyid, struct Buffer *keydata)
125 {
126   int rc = -1;
127   gpgme_ctx_t ctx = NULL;
128   gpgme_key_t key = NULL;
129 
130   if (create_gpgme_context(&ctx))
131     goto cleanup;
132 
133   if (gpgme_get_key(ctx, keyid, &key, 0))
134     goto cleanup;
135 
136   if (export_keydata(ctx, key, keydata))
137     goto cleanup;
138 
139   rc = 0;
140 cleanup:
141   gpgme_key_unref(key);
142   gpgme_release(ctx);
143   return rc;
144 }
145 #endif
146 
147 /**
148  * mutt_autocrypt_gpgme_create_key - Create a GPGME key
149  * @param addr    Email Address
150  * @param keyid   Key id
151  * @param keydata Key data
152  * @retval  0 Success
153  * @retval -1 Error
154  */
mutt_autocrypt_gpgme_create_key(struct Address * addr,struct Buffer * keyid,struct Buffer * keydata)155 int mutt_autocrypt_gpgme_create_key(struct Address *addr, struct Buffer *keyid,
156                                     struct Buffer *keydata)
157 {
158   int rc = -1;
159   gpgme_ctx_t ctx = NULL;
160   gpgme_genkey_result_t keyresult = NULL;
161   gpgme_key_t primary_key = NULL;
162   char buf[1024] = { 0 };
163 
164   /* GPGME says addresses should not be in idna form */
165   struct Address *copy = mutt_addr_copy(addr);
166   mutt_addr_to_local(copy);
167   mutt_addr_write(buf, sizeof(buf), copy, false);
168   mutt_addr_free(&copy);
169 
170   if (create_gpgme_context(&ctx))
171     goto cleanup;
172 
173   /* L10N: Message displayed just before a GPG key is generated for a created
174      autocrypt account.  */
175   mutt_message(_("Generating autocrypt key..."));
176 
177   /* Primary key */
178   gpgme_error_t err = gpgme_op_createkey(ctx, buf, "ed25519", 0, 0, NULL,
179                                          GPGME_CREATE_NOPASSWD | GPGME_CREATE_FORCE |
180                                              GPGME_CREATE_NOEXPIRE);
181   if (err)
182   {
183     /* L10N: GPGME was unable to generate a key for some reason.
184        %s is the error message returned by GPGME.  */
185     mutt_error(_("Error creating autocrypt key: %s"), gpgme_strerror(err));
186     goto cleanup;
187   }
188   keyresult = gpgme_op_genkey_result(ctx);
189   if (!keyresult->fpr)
190     goto cleanup;
191   mutt_buffer_strcpy(keyid, keyresult->fpr);
192   mutt_debug(LL_DEBUG1, "Generated key with id %s\n", mutt_buffer_string(keyid));
193 
194   /* Get gpgme_key_t to create the secondary key and export keydata */
195   err = gpgme_get_key(ctx, mutt_buffer_string(keyid), &primary_key, 0);
196   if (err)
197     goto cleanup;
198 
199   /* Secondary key */
200   err = gpgme_op_createsubkey(ctx, primary_key, "cv25519", 0, 0,
201                               GPGME_CREATE_NOPASSWD | GPGME_CREATE_NOEXPIRE);
202   if (err)
203   {
204     mutt_error(_("Error creating autocrypt key: %s"), gpgme_strerror(err));
205     goto cleanup;
206   }
207 
208   /* get keydata */
209   if (export_keydata(ctx, primary_key, keydata))
210     goto cleanup;
211   mutt_debug(LL_DEBUG1, "key has keydata *%s*\n", mutt_buffer_string(keydata));
212 
213   rc = 0;
214 
215 cleanup:
216   gpgme_key_unref(primary_key);
217   gpgme_release(ctx);
218   return rc;
219 }
220 
221 /**
222  * mutt_autocrypt_gpgme_select_key - Select a Autocrypt key
223  * @param[in]  keyid   Key id to select
224  * @param[out] keydata Buffer for resulting Key data
225  * @retval  0 Success
226  * @retval -1 Error
227  */
mutt_autocrypt_gpgme_select_key(struct Buffer * keyid,struct Buffer * keydata)228 int mutt_autocrypt_gpgme_select_key(struct Buffer *keyid, struct Buffer *keydata)
229 {
230   int rc = -1;
231   gpgme_ctx_t ctx = NULL;
232   gpgme_key_t key = NULL;
233 
234   OptAutocryptGpgme = true;
235   if (mutt_gpgme_select_secret_key(keyid))
236     goto cleanup;
237 
238   if (create_gpgme_context(&ctx))
239     goto cleanup;
240 
241   if (gpgme_get_key(ctx, mutt_buffer_string(keyid), &key, 0))
242     goto cleanup;
243 
244   if (key->revoked || key->expired || key->disabled || key->invalid ||
245       !key->can_encrypt || !key->can_sign)
246   {
247     /* L10N: After selecting a key for an autocrypt account,
248        this is displayed if the key was revoked/expired/disabled/invalid
249        or can't be used for both signing and encryption.
250        %s is the key fingerprint.  */
251     mutt_error(_("The key %s is not usable for autocrypt"), mutt_buffer_string(keyid));
252     goto cleanup;
253   }
254 
255   if (export_keydata(ctx, key, keydata))
256     goto cleanup;
257 
258   rc = 0;
259 
260 cleanup:
261   OptAutocryptGpgme = false;
262   gpgme_key_unref(key);
263   gpgme_release(ctx);
264   return rc;
265 }
266 
267 /**
268  * mutt_autocrypt_gpgme_select_or_create_key - Ask the user to select or create an Autocrypt key
269  * @param addr    Email Address
270  * @param keyid   Key id
271  * @param keydata Key data
272  * @retval  0 Success
273  * @retval -1 Error
274  */
mutt_autocrypt_gpgme_select_or_create_key(struct Address * addr,struct Buffer * keyid,struct Buffer * keydata)275 int mutt_autocrypt_gpgme_select_or_create_key(struct Address *addr, struct Buffer *keyid,
276                                               struct Buffer *keydata)
277 {
278   int rc = -1;
279 
280   /* L10N: During autocrypt account creation, this prompt asks the
281      user whether they want to create a new GPG key for the account,
282      or select an existing account from the keyring.  */
283   const char *prompt = _("(c)reate new, or (s)elect existing GPG key?");
284   /* L10N: The letters corresponding to the
285      "(c)reate new, or (s)elect existing GPG key?" prompt.  */
286   const char *letters = _("cs");
287 
288   int choice = mutt_multi_choice(prompt, letters);
289   switch (choice)
290   {
291     case 2: /* select existing */
292       rc = mutt_autocrypt_gpgme_select_key(keyid, keydata);
293       if (rc == 0)
294         break;
295 
296       /* L10N: During autocrypt account creation, if selecting an existing key fails
297          for some reason, we prompt to see if they want to create a key instead.  */
298       if (mutt_yesorno(_("Create a new GPG key for this account, instead?"), MUTT_YES) != MUTT_YES)
299         break;
300       /* fallthrough */
301 
302     case 1: /* create new */
303       rc = mutt_autocrypt_gpgme_create_key(addr, keyid, keydata);
304   }
305 
306   return rc;
307 }
308 
309 /**
310  * mutt_autocrypt_gpgme_import_key - Read a key from GPGME
311  * @param keydata Buffer for key data
312  * @param keyid   Buffer for key id
313  * @retval  0 Success
314  * @retval -1 Error
315  */
mutt_autocrypt_gpgme_import_key(const char * keydata,struct Buffer * keyid)316 int mutt_autocrypt_gpgme_import_key(const char *keydata, struct Buffer *keyid)
317 {
318   int rc = -1;
319   gpgme_ctx_t ctx = NULL;
320   gpgme_data_t dh = NULL;
321 
322   if (create_gpgme_context(&ctx))
323     goto cleanup;
324 
325   struct Buffer *raw_keydata = mutt_buffer_pool_get();
326   if (!mutt_b64_buffer_decode(raw_keydata, keydata))
327     goto cleanup;
328 
329   if (gpgme_data_new_from_mem(&dh, mutt_buffer_string(raw_keydata),
330                               mutt_buffer_len(raw_keydata), 0))
331   {
332     goto cleanup;
333   }
334 
335   if (gpgme_op_import(ctx, dh))
336     goto cleanup;
337 
338   gpgme_import_result_t result = gpgme_op_import_result(ctx);
339   if (!result->imports || !result->imports->fpr)
340     goto cleanup;
341   mutt_buffer_strcpy(keyid, result->imports->fpr);
342 
343   rc = 0;
344 
345 cleanup:
346   gpgme_data_release(dh);
347   gpgme_release(ctx);
348   mutt_buffer_pool_release(&raw_keydata);
349   return rc;
350 }
351 
352 /**
353  * mutt_autocrypt_gpgme_is_valid_key - Is a key id valid?
354  * @param keyid Key id to check
355  * @retval true Key id is valid
356  */
mutt_autocrypt_gpgme_is_valid_key(const char * keyid)357 bool mutt_autocrypt_gpgme_is_valid_key(const char *keyid)
358 {
359   bool rc = false;
360   gpgme_ctx_t ctx = NULL;
361   gpgme_key_t key = NULL;
362 
363   if (!keyid)
364     return false;
365 
366   if (create_gpgme_context(&ctx))
367     goto cleanup;
368 
369   if (gpgme_get_key(ctx, keyid, &key, 0))
370     goto cleanup;
371 
372   rc = true;
373   if (key->revoked || key->expired || key->disabled || key->invalid || !key->can_encrypt)
374     rc = false;
375 
376 cleanup:
377   gpgme_key_unref(key);
378   gpgme_release(ctx);
379   return rc;
380 }
381