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(©);
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