Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2010 Stefan Walter
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU Lesser General Public License as
8 : : * published by the Free Software Foundation; either version 2.1 of
9 : : * the License, or (at your option) any later version.
10 : : *
11 : : * This program is distributed in the hope that it will be useful, but
12 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : : */
19 : :
20 : : #include "config.h"
21 : :
22 : : #include "gcr-secret-exchange.h"
23 : :
24 : : #include "egg/egg-dh.h"
25 : : #include "egg/egg-hkdf.h"
26 : : #include "egg/egg-libgcrypt.h"
27 : : #include "egg/egg-padding.h"
28 : : #include "egg/egg-secure-memory.h"
29 : :
30 : : #include <string.h>
31 : : #include <gcrypt.h>
32 : :
33 : 78 : EGG_SECURE_DECLARE (secret_exchange);
34 : :
35 : : /**
36 : : * GcrSecretExchange:
37 : : *
38 : : * Allows exchange of secrets between two processes on the same system without
39 : : * exposing those secrets to things like loggers, non-pageable memory etc.
40 : : *
41 : : * This does not protect against active attacks like MITM attacks.
42 : : *
43 : : * Each side creates a secret exchange object, and one of the sides calls
44 : : * [method@SecretExchange.begin]. This creates a string, which should be passed
45 : : * to the other side. Each side passes the strings it receives into
46 : : * [method@SecretExchange.receive].
47 : : *
48 : : * In order to send a reply (either with or without a secret) use
49 : : * [method@SecretExchange.send]. A side must have successfully called
50 : : * [method@SecretExchange.receive] before it can use
51 : : * [method@SecretExchange.send].
52 : : *
53 : : * The secret exchange objects can be used for multiple iterations of the
54 : : * conversation, or for just one request/reply. The only limitation being that
55 : : * the initial request cannot contain a secret.
56 : : *
57 : : * Caveat: Information about the approximate length (rounded up to the nearest
58 : : * 16 bytes) may be leaked. If this is considered inacceptable, do not use
59 : : * [class@SecretExchange].
60 : : */
61 : :
62 : : /**
63 : : * GCR_SECRET_EXCHANGE_PROTOCOL_1:
64 : : *
65 : : * The current secret exchange protocol. Key agreement is done using DH with the
66 : : * 1536 bit IKE parameter group. Keys are derived using SHA256 with HKDF. The
67 : : * transport encryption is done with 128 bit AES.
68 : : */
69 : :
70 : : #define SECRET_EXCHANGE_PROTOCOL_1_PREFIX "[" GCR_SECRET_EXCHANGE_PROTOCOL_1 "]\n"
71 : :
72 : : enum {
73 : : PROP_0,
74 : : PROP_PROTOCOL
75 : : };
76 : :
77 : : typedef struct _GcrSecretExchangeDefault GcrSecretExchangeDefault;
78 : :
79 : : struct _GcrSecretExchangePrivate {
80 : : GcrSecretExchangeDefault *default_exchange;
81 : : GDestroyNotify destroy_exchange;
82 : : gboolean explicit_protocol;
83 : : gboolean generated;
84 : : guchar *publi;
85 : : gsize n_publi;
86 : : gboolean derived;
87 : : gchar *secret;
88 : : gsize n_secret;
89 : : };
90 : :
91 [ + + + - : 293 : G_DEFINE_TYPE_WITH_PRIVATE (GcrSecretExchange, gcr_secret_exchange, G_TYPE_OBJECT);
+ + ]
92 : :
93 : : static void
94 : 68 : key_file_set_base64 (GKeyFile *key_file, const gchar *section,
95 : : const gchar *field, gconstpointer data, gsize n_data)
96 : : {
97 : : gchar *value;
98 : :
99 : 68 : value = g_base64_encode (data, n_data);
100 : 68 : g_key_file_set_value (key_file, section, field, value);
101 : 68 : g_free (value);
102 : 68 : }
103 : :
104 : : static gpointer
105 : 54 : key_file_get_base64 (GKeyFile *key_file, const gchar *section,
106 : : const gchar *field, gsize *n_result)
107 : : {
108 : 54 : gpointer result = NULL;
109 : : gchar *data;
110 : :
111 [ - + ]: 54 : g_return_val_if_fail (key_file, NULL);
112 [ - + ]: 54 : g_return_val_if_fail (section, NULL);
113 [ - + ]: 54 : g_return_val_if_fail (field, NULL);
114 [ - + ]: 54 : g_return_val_if_fail (n_result, NULL);
115 : :
116 : 54 : data = g_key_file_get_value (key_file, section, field, NULL);
117 [ + - ]: 54 : if (data != NULL)
118 : 54 : result = g_base64_decode (data, n_result);
119 : 54 : g_free (data);
120 : 54 : return result;
121 : : }
122 : :
123 : : static void
124 : 42 : gcr_secret_exchange_init (GcrSecretExchange *self)
125 : : {
126 : 42 : self->pv = gcr_secret_exchange_get_instance_private (self);
127 : 42 : }
128 : :
129 : :
130 : : static void
131 : 42 : gcr_secret_exchange_set_property (GObject *obj,
132 : : guint prop_id,
133 : : const GValue *value,
134 : : GParamSpec *pspec)
135 : : {
136 : 42 : GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
137 : : const gchar *protocol;
138 : :
139 [ + - ]: 42 : switch (prop_id) {
140 : 42 : case PROP_PROTOCOL:
141 : 42 : protocol = g_value_get_string (value);
142 [ + - ]: 42 : if (protocol == NULL) {
143 : 42 : g_debug ("automatically selecting secret exchange protocol");
144 : :
145 : : } else {
146 [ # # ]: 0 : if (g_str_equal (protocol, GCR_SECRET_EXCHANGE_PROTOCOL_1)) {
147 : 0 : g_debug ("explicitly using secret exchange protocol: %s",
148 : : GCR_SECRET_EXCHANGE_PROTOCOL_1);
149 : 0 : self->pv->explicit_protocol = TRUE;
150 : : } else {
151 : 0 : g_warning ("the GcrSecretExchange protocol %s is unsupported defaulting to %s",
152 : : protocol, GCR_SECRET_EXCHANGE_PROTOCOL_1);
153 : : }
154 : : }
155 : 42 : break;
156 : 0 : default:
157 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
158 : 0 : break;
159 : : }
160 : 42 : }
161 : :
162 : : static void
163 : 0 : gcr_secret_exchange_get_property (GObject *obj,
164 : : guint prop_id,
165 : : GValue *value,
166 : : GParamSpec *pspec)
167 : : {
168 : 0 : GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
169 : :
170 [ # # ]: 0 : switch (prop_id) {
171 : 0 : case PROP_PROTOCOL:
172 : 0 : g_value_set_string (value, gcr_secret_exchange_get_protocol (self));
173 : 0 : break;
174 : 0 : default:
175 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
176 : 0 : break;
177 : : }
178 : 0 : }
179 : :
180 : : static void
181 : 61 : clear_secret_exchange (GcrSecretExchange *self)
182 : : {
183 : 61 : g_free (self->pv->publi);
184 : 61 : self->pv->publi = NULL;
185 : 61 : self->pv->n_publi = 0;
186 : 61 : self->pv->derived = FALSE;
187 : 61 : self->pv->generated = TRUE;
188 : 61 : egg_secure_free (self->pv->secret);
189 : 61 : self->pv->secret = NULL;
190 : 61 : self->pv->n_secret = 0;
191 : 61 : }
192 : :
193 : : static void
194 : 40 : gcr_secret_exchange_finalize (GObject *obj)
195 : : {
196 : 40 : GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
197 : :
198 [ + - ]: 40 : if (self->pv->destroy_exchange)
199 : 40 : (self->pv->destroy_exchange) (self->pv->default_exchange);
200 : :
201 : 40 : clear_secret_exchange (self);
202 : :
203 : 40 : G_OBJECT_CLASS (gcr_secret_exchange_parent_class)->finalize (obj);
204 : 40 : }
205 : :
206 : : /**
207 : : * gcr_secret_exchange_new:
208 : : * @protocol: (nullable): the exchange protocol to use
209 : : *
210 : : * Create a new secret exchange object.
211 : : *
212 : : * Specify a protocol of %NULL to allow any protocol. This is especially
213 : : * relevant on the side of the exchange that does not call
214 : : * [method@SecretExchange.begin], that is the originator. Currently the only
215 : : * protocol supported is %GCR_SECRET_EXCHANGE_PROTOCOL_1.
216 : : *
217 : : * Returns: (transfer full): A new #GcrSecretExchange object
218 : : */
219 : : GcrSecretExchange *
220 : 42 : gcr_secret_exchange_new (const gchar *protocol)
221 : : {
222 : 42 : return g_object_new (GCR_TYPE_SECRET_EXCHANGE,
223 : : "protocol", protocol,
224 : : NULL);
225 : : }
226 : :
227 : : /**
228 : : * gcr_secret_exchange_get_protocol:
229 : : * @self: a #GcrSecretExchange object
230 : : * Get the secret exchange protocol.
231 : : *
232 : : * Will return %NULL if no protocol was specified, and either
233 : : * [method@SecretExchange.begin] or [method@SecretExchange.receive] have not
234 : : * been called successfully.
235 : : *
236 : : * Returns: the protocol or %NULL
237 : : */
238 : : const gchar *
239 : 0 : gcr_secret_exchange_get_protocol (GcrSecretExchange *self)
240 : : {
241 [ # # # # : 0 : g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
# # # # ]
242 [ # # # # ]: 0 : if (self->pv->explicit_protocol || self->pv->generated)
243 : 0 : return GCR_SECRET_EXCHANGE_PROTOCOL_1;
244 : 0 : return NULL;
245 : : }
246 : :
247 : : /**
248 : : * gcr_secret_exchange_begin:
249 : : * @self: a #GcrSecretExchange object
250 : : *
251 : : * Begin the secret exchange. The resulting string should be sent to the other
252 : : * side of the exchange. The other side should use [method@SecretExchange.receive]
253 : : * to process the string.
254 : : *
255 : : * Returns: (transfer full): A newly allocated string to be sent to the other
256 : : * side of the secret exchange
257 : : */
258 : : gchar *
259 : 21 : gcr_secret_exchange_begin (GcrSecretExchange *self)
260 : : {
261 : : GcrSecretExchangeClass *klass;
262 : : GKeyFile *output;
263 : : gchar *result;
264 : :
265 [ - + + - : 21 : g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
+ - - + ]
266 : :
267 : 21 : klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
268 [ - + ]: 21 : g_return_val_if_fail (klass->generate_exchange_key, NULL);
269 : :
270 : 21 : clear_secret_exchange (self);
271 : :
272 : 21 : output = g_key_file_new ();
273 : :
274 [ - + ]: 21 : if (!(klass->generate_exchange_key) (self, GCR_SECRET_EXCHANGE_PROTOCOL_1,
275 : 21 : &self->pv->publi, &self->pv->n_publi))
276 : 0 : g_return_val_if_reached (NULL);
277 : 21 : self->pv->generated = TRUE;
278 : :
279 : 21 : key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public",
280 : 21 : self->pv->publi, self->pv->n_publi);
281 : :
282 : 21 : result = g_key_file_to_data (output, NULL, NULL);
283 [ - + ]: 21 : g_return_val_if_fail (result != NULL, NULL);
284 : :
285 : 21 : g_strchug (result);
286 : :
287 : 21 : gchar *string = g_strescape (result, "");
288 : 21 : g_debug ("beginning the secret exchange: %s", string);
289 : 21 : g_free (string);
290 : :
291 [ - + ]: 21 : if (!g_str_has_prefix (result, SECRET_EXCHANGE_PROTOCOL_1_PREFIX))
292 : 0 : g_warning ("the prepared data does not have the correct protocol prefix");
293 : :
294 : 21 : g_key_file_free (output);
295 : :
296 : 21 : return result;
297 : : }
298 : :
299 : : static gboolean
300 : 38 : derive_key (GcrSecretExchange *self,
301 : : GKeyFile *input)
302 : : {
303 : : GcrSecretExchangeClass *klass;
304 : : gboolean ret;
305 : : guchar *peer;
306 : : gsize n_peer;
307 : :
308 : 38 : klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
309 [ - + ]: 38 : g_return_val_if_fail (klass->derive_transport_key, FALSE);
310 : :
311 : 38 : g_debug ("deriving shared transport key");
312 : :
313 : 38 : peer = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public", &n_peer);
314 [ - + ]: 38 : if (peer == NULL) {
315 : 0 : g_message ("secret-exchange: invalid or missing 'public' argument");
316 : 0 : return FALSE;
317 : : }
318 : :
319 : 38 : ret = (klass->derive_transport_key) (self, peer, n_peer);
320 : 38 : self->pv->derived = ret;
321 : :
322 : 38 : g_free (peer);
323 : 38 : return ret;
324 : : }
325 : :
326 : : static gboolean
327 : 8 : perform_decrypt (GcrSecretExchange *self,
328 : : GKeyFile *input,
329 : : guchar **secret,
330 : : gsize *n_secret)
331 : : {
332 : : GcrSecretExchangeClass *klass;
333 : : gpointer iv, value;
334 : : guchar *result;
335 : : gsize n_result, n_iv, n_value;
336 : : gboolean ret;
337 : :
338 : 8 : klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
339 [ - + ]: 8 : g_return_val_if_fail (klass->decrypt_transport_data, FALSE);
340 : :
341 : 8 : iv = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "iv", &n_iv);
342 : :
343 : 8 : value = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", &n_value);
344 [ - + ]: 8 : if (value == NULL) {
345 : 0 : g_message ("secret-exchange: invalid or missing value");
346 : 0 : g_free (iv);
347 : 0 : return FALSE;
348 : : }
349 : :
350 : 8 : ret = (klass->decrypt_transport_data) (self, egg_secure_realloc, value, n_value,
351 : : iv, n_iv, &result, &n_result);
352 : :
353 : 8 : g_free (value);
354 : 8 : g_free (iv);
355 : :
356 [ - + ]: 8 : if (!ret)
357 : 0 : return FALSE;
358 : :
359 : : /* Reallocate a null terminator */
360 [ + - ]: 8 : if (result) {
361 : 8 : result = egg_secure_realloc (result, n_result + 1);
362 : 8 : result[n_result] = 0;
363 : : }
364 : :
365 : 8 : *secret = result;
366 : 8 : *n_secret = n_result;
367 : :
368 : 8 : return TRUE;
369 : : }
370 : :
371 : : /**
372 : : * gcr_secret_exchange_receive:
373 : : * @self: a #GcrSecretExchange object
374 : : * @exchange: the string received
375 : : *
376 : : * Receive a string from the other side of secret exchange. This string will
377 : : * have been created by [method@SecretExchange.begin] or
378 : : * [method@SecretExchange.send].
379 : : *
380 : : * After this call completes successfully the value returned from
381 : : * gcr_secret_exchange_get_secret() will have changed.
382 : : *
383 : : * Returns: whether the string was successfully parsed and received
384 : : */
385 : : gboolean
386 : 52 : gcr_secret_exchange_receive (GcrSecretExchange *self,
387 : : const gchar *exchange)
388 : : {
389 : : GcrSecretExchangeClass *klass;
390 : 52 : gchar *secret = NULL;
391 : 52 : gsize n_secret = 0;
392 : : GKeyFile *input;
393 : : gboolean ret;
394 : :
395 [ - + + - : 52 : g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), FALSE);
+ - - + ]
396 [ - + ]: 52 : g_return_val_if_fail (exchange != NULL, FALSE);
397 : :
398 : 52 : klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
399 [ - + ]: 52 : g_return_val_if_fail (klass->generate_exchange_key, FALSE);
400 [ - + ]: 52 : g_return_val_if_fail (klass->derive_transport_key, FALSE);
401 : :
402 : 52 : gchar *string = g_strescape (exchange, "");
403 : 52 : g_debug ("receiving secret exchange: %s", string);
404 : 52 : g_free (string);
405 : :
406 : : /* Parse the input */
407 : 52 : input = g_key_file_new ();
408 [ - + ]: 52 : if (!g_key_file_load_from_data (input, exchange, strlen (exchange),
409 : : G_KEY_FILE_NONE, NULL)) {
410 : 0 : g_key_file_free (input);
411 : 0 : g_message ("couldn't parse secret exchange data");
412 : 0 : return FALSE;
413 : : }
414 : :
415 [ + + ]: 52 : if (!self->pv->generated) {
416 [ - + ]: 21 : if (!(klass->generate_exchange_key) (self, GCR_SECRET_EXCHANGE_PROTOCOL_1,
417 : 21 : &self->pv->publi, &self->pv->n_publi))
418 : 0 : g_return_val_if_reached (FALSE);
419 : 21 : self->pv->generated = TRUE;
420 : : }
421 : :
422 : 52 : ret = TRUE;
423 : :
424 [ + + ]: 52 : if (!self->pv->derived) {
425 [ - + ]: 38 : if (!derive_key (self, input))
426 : 0 : ret = FALSE;
427 : : }
428 : :
429 [ + - + + ]: 52 : if (ret && g_key_file_has_key (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", NULL))
430 : 8 : ret = perform_decrypt (self, input, (guchar **)&secret, &n_secret);
431 : :
432 [ + - ]: 52 : if (ret) {
433 : 52 : egg_secure_free (self->pv->secret);
434 : 52 : self->pv->secret = secret;
435 : 52 : self->pv->n_secret = n_secret;
436 : : }
437 : :
438 : 52 : g_key_file_free (input);
439 : 52 : return ret;
440 : : }
441 : :
442 : : /**
443 : : * gcr_secret_exchange_get_secret:
444 : : * @self: a #GcrSecretExchange object
445 : : * @secret_len: (out) (optional): optionally, a location to store the length of returned secret
446 : : *
447 : : * Returns the last secret received. If no secret has yet been received this
448 : : * will return %NULL. The string is owned by the #GcrSecretExchange object
449 : : * and will be valid until the next time that gcr_secret_exchange_receive()
450 : : * is called on this object, or the object is destroyed.
451 : : *
452 : : * Depending on the secret passed into the other side of the secret exchange,
453 : : * the result may be a binary string. It does however have a null terminator,
454 : : * so if you're certain that it is does not contain arbitrary binary data,
455 : : * it can be used as a string.
456 : : *
457 : : * Returns: (transfer none) (array length=secret_len): the last secret received
458 : : */
459 : : const gchar *
460 : 11 : gcr_secret_exchange_get_secret (GcrSecretExchange *self,
461 : : gsize *secret_len)
462 : : {
463 [ - + + - : 11 : g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
+ - - + ]
464 : :
465 [ - + ]: 11 : if (secret_len)
466 : 0 : *secret_len = self->pv->n_secret;
467 : 11 : return self->pv->secret;
468 : : }
469 : :
470 : : static gboolean
471 : 8 : perform_encrypt (GcrSecretExchange *self,
472 : : GKeyFile *output,
473 : : const gchar *secret,
474 : : gsize n_secret)
475 : : {
476 : : GcrSecretExchangeClass *klass;
477 : : guchar *result, *iv;
478 : : gsize n_result, n_iv;
479 : :
480 : 8 : klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
481 [ - + ]: 8 : g_return_val_if_fail (klass->encrypt_transport_data, FALSE);
482 : :
483 [ - + ]: 8 : if (!(klass->encrypt_transport_data) (self, g_realloc, (const guchar *)secret,
484 : : n_secret, &iv, &n_iv, &result, &n_result))
485 : 0 : return FALSE;
486 : :
487 : 8 : key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", result, n_result);
488 : 8 : key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "iv", iv, n_iv);
489 : :
490 : 8 : g_free (result);
491 : 8 : g_free (iv);
492 : :
493 : 8 : return TRUE;
494 : : }
495 : :
496 : : /**
497 : : * gcr_secret_exchange_send:
498 : : * @self: a #GcrSecretExchange object
499 : : * @secret: (nullable): optionally, a secret to send to the other side
500 : : * @secret_len: length of @secret, or -1 if null terminated
501 : : *
502 : : * Send a reply to the other side of the secret exchange, optionally sending a
503 : : * secret.
504 : : *
505 : : * [method@SecretExchange.receive] must have been successfully called at least
506 : : * once on this object. In other words this object must have received data
507 : : * from the other side of the secret exchange, before we can send a secret.
508 : : *
509 : : * Returns: (transfer full): a newly allocated string to be sent to the other
510 : : * side of the secret exchange
511 : : */
512 : : gchar *
513 : 31 : gcr_secret_exchange_send (GcrSecretExchange *self,
514 : : const gchar *secret,
515 : : gssize secret_len)
516 : : {
517 : : GKeyFile *output;
518 : : gchar *result;
519 : :
520 [ - + + - : 31 : g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
+ - - + ]
521 : :
522 [ - + ]: 31 : if (!self->pv->derived) {
523 : 0 : g_warning ("gcr_secret_exchange_receive() must be called "
524 : : "before calling this function");
525 : 0 : return NULL;
526 : : }
527 : :
528 : 31 : output = g_key_file_new ();
529 : 31 : key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public", self->pv->publi,
530 : 31 : self->pv->n_publi);
531 : :
532 [ + + ]: 31 : if (secret != NULL) {
533 [ + - ]: 8 : if (secret_len < 0)
534 : 8 : secret_len = strlen (secret);
535 [ - + ]: 8 : if (!perform_encrypt (self, output, secret, secret_len)) {
536 : 0 : g_key_file_free (output);
537 : 0 : return NULL;
538 : : }
539 : : }
540 : :
541 : 31 : result = g_key_file_to_data (output, NULL, NULL);
542 [ - + ]: 31 : g_return_val_if_fail (result != NULL, NULL);
543 : :
544 : 31 : g_strchug (result);
545 : :
546 : 31 : gchar *string = g_strescape (result, "");
547 : 31 : g_debug ("sending the secret exchange: %s", string);
548 : 31 : g_free (string);
549 : :
550 [ - + ]: 31 : if (!g_str_has_prefix (result, SECRET_EXCHANGE_PROTOCOL_1_PREFIX))
551 : 0 : g_warning ("the prepared data does not have the correct protocol prefix: %s", result);
552 : :
553 : 31 : g_key_file_free (output);
554 : 31 : return result;
555 : : }
556 : :
557 : : /*
558 : : * This is the only set we support so far. It includes:
559 : : * - DH with the 1536 ike modp group for key exchange
560 : : * - HKDF SHA256 for hashing of the key to appropriate size
561 : : * - AES 128 CBC for encryption
562 : : * - PKCS#7 style padding
563 : : */
564 : :
565 : : #define EXCHANGE_1_IKE_NAME "ietf-ike-grp-modp-1536"
566 : : #define EXCHANGE_1_KEY_LENGTH 16
567 : : #define EXCHANGE_1_IV_LENGTH 16
568 : : #define EXCHANGE_1_HASH_ALGO "sha256"
569 : : #define EXCHANGE_1_CIPHER_ALGO GCRY_CIPHER_AES128
570 : : #define EXCHANGE_1_CIPHER_MODE GCRY_CIPHER_MODE_CBC
571 : :
572 : : struct _GcrSecretExchangeDefault {
573 : : gcry_mpi_t prime;
574 : : gcry_mpi_t base;
575 : : gcry_mpi_t pub;
576 : : gcry_mpi_t priv;
577 : : gpointer key;
578 : : };
579 : :
580 : : static guchar *
581 : 42 : mpi_to_data (gcry_mpi_t mpi,
582 : : gsize *n_data)
583 : : {
584 : : gcry_error_t gcry;
585 : : guchar *data;
586 : :
587 : : /* Get the size */
588 : 42 : gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, n_data, mpi);
589 [ - + ]: 42 : g_return_val_if_fail (gcry == 0, NULL);
590 : :
591 : 42 : data = g_malloc0 (*n_data);
592 : :
593 : : /* Write into buffer */
594 : 42 : gcry = gcry_mpi_print (GCRYMPI_FMT_USG, data, *n_data, n_data, mpi);
595 [ - + ]: 42 : g_return_val_if_fail (gcry == 0, NULL);
596 : :
597 : 42 : return data;
598 : : }
599 : :
600 : : static gcry_mpi_t
601 : 38 : mpi_from_data (const guchar *data,
602 : : gsize n_data)
603 : : {
604 : : gcry_mpi_t mpi;
605 : : gcry_error_t gcry;
606 : :
607 : 38 : gcry = gcry_mpi_scan (&mpi, GCRYMPI_FMT_USG, data, n_data, NULL);
608 [ + - ]: 38 : return (gcry == 0) ? mpi : NULL;
609 : : }
610 : :
611 : : static void
612 : 40 : gcr_secret_exchange_default_free (gpointer to_free)
613 : : {
614 : 40 : GcrSecretExchangeDefault *data = to_free;
615 : 40 : gcry_mpi_release (data->prime);
616 : 40 : gcry_mpi_release (data->base);
617 : 40 : gcry_mpi_release (data->pub);
618 : 40 : gcry_mpi_release (data->priv);
619 [ + + ]: 40 : if (data->key) {
620 : 36 : egg_secure_clear (data->key, EXCHANGE_1_KEY_LENGTH);
621 : 36 : egg_secure_free (data->key);
622 : : }
623 : 40 : g_free (data);
624 : 40 : }
625 : :
626 : : static gboolean
627 : 42 : gcr_secret_exchange_default_generate_exchange_key (GcrSecretExchange *exchange,
628 : : const gchar *scheme,
629 : : guchar **public_key,
630 : : gsize *n_public_key)
631 : : {
632 : 42 : GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
633 : :
634 : 42 : g_debug ("generating public key");
635 : :
636 [ + - ]: 42 : if (data == NULL) {
637 : 42 : data = g_new0 (GcrSecretExchangeDefault, 1);
638 [ - + ]: 42 : if (!egg_dh_default_params (EXCHANGE_1_IKE_NAME, &data->prime, &data->base))
639 : 0 : g_return_val_if_reached (FALSE);
640 : :
641 : 42 : exchange->pv->default_exchange = data;
642 : 42 : exchange->pv->destroy_exchange = gcr_secret_exchange_default_free;
643 : : }
644 : :
645 : 42 : gcry_mpi_release (data->priv);
646 : 42 : data->priv = NULL;
647 : 42 : gcry_mpi_release (data->pub);
648 : 42 : data->pub = NULL;
649 : 42 : egg_secure_free (data->key);
650 : 42 : data->key = NULL;
651 : :
652 [ - + ]: 42 : if (!egg_dh_gen_pair (data->prime, data->base, 0,
653 : : &data->pub, &data->priv))
654 : 0 : g_return_val_if_reached (FALSE);
655 : :
656 : 42 : *public_key = mpi_to_data (data->pub, n_public_key);
657 : 42 : return *public_key != NULL;
658 : : }
659 : :
660 : : static gboolean
661 : 38 : gcr_secret_exchange_default_derive_transport_key (GcrSecretExchange *exchange,
662 : : const guchar *peer,
663 : : gsize n_peer)
664 : : {
665 : 38 : GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
666 : : gpointer ikm;
667 : : gsize n_ikm;
668 : : gcry_mpi_t mpi;
669 : :
670 : 38 : g_debug ("deriving transport key");
671 : :
672 [ - + ]: 38 : g_return_val_if_fail (data != NULL, FALSE);
673 [ - + ]: 38 : g_return_val_if_fail (data->priv != NULL, FALSE);
674 : :
675 : 38 : mpi = mpi_from_data (peer, n_peer);
676 [ - + ]: 38 : if (mpi == NULL) {
677 : 0 : g_debug ("invalid peer mpi");
678 : 0 : return FALSE;
679 : : }
680 : :
681 : : /* Build up a key we can use */
682 : 38 : ikm = egg_dh_gen_secret (mpi, data->priv, data->prime, &n_ikm);
683 [ - + ]: 38 : g_return_val_if_fail (ikm != NULL, FALSE);
684 : :
685 [ + - ]: 38 : if (data->key == NULL)
686 : 38 : data->key = egg_secure_alloc (EXCHANGE_1_KEY_LENGTH);
687 : :
688 [ - + ]: 38 : if (!egg_hkdf_perform (EXCHANGE_1_HASH_ALGO, ikm, n_ikm, NULL, 0,
689 : : NULL, 0, data->key, EXCHANGE_1_KEY_LENGTH))
690 : 0 : g_return_val_if_reached (FALSE);
691 : :
692 : 38 : egg_secure_free (ikm);
693 : 38 : gcry_mpi_release (mpi);
694 : :
695 : 38 : return TRUE;
696 : : }
697 : :
698 : : static gboolean
699 : 8 : gcr_secret_exchange_default_encrypt_transport_data (GcrSecretExchange *exchange,
700 : : GckAllocator allocator,
701 : : const guchar *plain_text,
702 : : gsize n_plain_text,
703 : : guchar **iv,
704 : : gsize *n_iv,
705 : : guchar **cipher_text,
706 : : gsize *n_cipher_text)
707 : : {
708 : 8 : GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
709 : : gcry_cipher_hd_t cih;
710 : : gcry_error_t gcry;
711 : : guchar *padded;
712 : : gsize n_result;
713 : : guchar *result;
714 : : gsize pos;
715 : :
716 [ - + ]: 8 : g_return_val_if_fail (data != NULL, FALSE);
717 [ - + ]: 8 : g_return_val_if_fail (data->key != NULL, FALSE);
718 : :
719 : 8 : g_debug ("encrypting data");
720 : :
721 : 8 : gcry = gcry_cipher_open (&cih, EXCHANGE_1_CIPHER_ALGO, EXCHANGE_1_CIPHER_MODE, 0);
722 [ - + ]: 8 : if (gcry != 0) {
723 : 0 : g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
724 : 0 : g_free (iv);
725 : 0 : return FALSE;
726 : : }
727 : :
728 : 8 : *iv = (allocator) (NULL, EXCHANGE_1_IV_LENGTH);
729 [ - + ]: 8 : g_return_val_if_fail (*iv != NULL, FALSE);
730 : 8 : gcry_create_nonce (*iv, EXCHANGE_1_IV_LENGTH);
731 : 8 : *n_iv = EXCHANGE_1_IV_LENGTH;
732 : :
733 : : /* 16 = 128 bits */
734 : 8 : gcry = gcry_cipher_setkey (cih, data->key, EXCHANGE_1_KEY_LENGTH);
735 [ - + ]: 8 : g_return_val_if_fail (gcry == 0, FALSE);
736 : :
737 : : /* 16 = 128 bits */
738 : 8 : gcry = gcry_cipher_setiv (cih, *iv, EXCHANGE_1_IV_LENGTH);
739 [ - + ]: 8 : g_return_val_if_fail (gcry == 0, FALSE);
740 : :
741 : : /* Pad the text properly */
742 [ - + ]: 8 : if (!egg_padding_pkcs7_pad (egg_secure_realloc, 16, plain_text, n_plain_text,
743 : : (gpointer*)&padded, &n_result))
744 : 0 : g_return_val_if_reached (FALSE);
745 : 8 : result = (allocator) (NULL, n_result);
746 [ - + ]: 8 : g_return_val_if_fail (result != NULL, FALSE);
747 : :
748 [ + + ]: 16 : for (pos = 0; pos < n_result; pos += 16) {
749 : 8 : gcry = gcry_cipher_encrypt (cih, result + pos, 16, padded + pos, 16);
750 [ - + ]: 8 : g_return_val_if_fail (gcry == 0, FALSE);
751 : : }
752 : :
753 : 8 : gcry_cipher_close (cih);
754 : :
755 : 8 : egg_secure_clear (padded, n_result);
756 : 8 : egg_secure_free (padded);
757 : :
758 : 8 : *cipher_text = result;
759 : 8 : *n_cipher_text = n_result;
760 : 8 : return TRUE;
761 : : }
762 : :
763 : : static gboolean
764 : 8 : gcr_secret_exchange_default_decrypt_transport_data (GcrSecretExchange *exchange,
765 : : GckAllocator allocator,
766 : : const guchar *cipher_text,
767 : : gsize n_cipher_text,
768 : : const guchar *iv,
769 : : gsize n_iv,
770 : : guchar **plain_text,
771 : : gsize *n_plain_text)
772 : : {
773 : 8 : GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
774 : : guchar* padded;
775 : : guchar* result;
776 : : gsize n_result;
777 : : gsize pos;
778 : : gcry_cipher_hd_t cih;
779 : : gcry_error_t gcry;
780 : :
781 [ - + ]: 8 : g_return_val_if_fail (data != NULL, FALSE);
782 [ - + ]: 8 : g_return_val_if_fail (data->key != NULL, FALSE);
783 : :
784 : 8 : g_debug ("decrypting data");
785 : :
786 [ + - - + ]: 8 : if (iv == NULL || n_iv != EXCHANGE_1_IV_LENGTH) {
787 : 0 : g_message ("secret-exchange: invalid or missing iv");
788 : 0 : return FALSE;
789 : : }
790 : :
791 [ - + ]: 8 : if (n_cipher_text % 16 != 0) {
792 : 0 : g_message ("secret-message: invalid length for cipher text");
793 : 0 : return FALSE;
794 : : }
795 : :
796 : 8 : gcry = gcry_cipher_open (&cih, EXCHANGE_1_CIPHER_ALGO, EXCHANGE_1_CIPHER_MODE, 0);
797 [ - + ]: 8 : if (gcry != 0) {
798 : 0 : g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
799 : 0 : return FALSE;
800 : : }
801 : :
802 : : /* 16 = 128 bits */
803 : 8 : gcry = gcry_cipher_setkey (cih, data->key, EXCHANGE_1_KEY_LENGTH);
804 [ - + ]: 8 : g_return_val_if_fail (gcry == 0, FALSE);
805 : :
806 : : /* 16 = 128 bits */
807 : 8 : gcry = gcry_cipher_setiv (cih, iv, n_iv);
808 [ - + ]: 8 : g_return_val_if_fail (gcry == 0, FALSE);
809 : :
810 : : /* Allocate memory for the result */
811 : 8 : padded = (allocator) (NULL, n_cipher_text);
812 [ - + ]: 8 : g_return_val_if_fail (padded != NULL, FALSE);
813 : :
814 [ + + ]: 16 : for (pos = 0; pos < n_cipher_text; pos += 16) {
815 : 8 : gcry = gcry_cipher_decrypt (cih, padded + pos, 16, (guchar *)cipher_text + pos, 16);
816 [ - + ]: 8 : g_return_val_if_fail (gcry == 0, FALSE);
817 : : }
818 : :
819 : 8 : gcry_cipher_close (cih);
820 : :
821 [ - + ]: 8 : if (!egg_padding_pkcs7_unpad (allocator, 16, padded, n_cipher_text,
822 : : (gpointer*)&result, &n_result))
823 : 0 : result = NULL;
824 : :
825 : : /* Free the padded text */
826 : 8 : (allocator) (padded, 0);
827 : :
828 : 8 : *plain_text = result;
829 : 8 : *n_plain_text = n_result;
830 : 8 : return TRUE;
831 : : }
832 : :
833 : : static void
834 : 2 : gcr_secret_exchange_class_init (GcrSecretExchangeClass *klass)
835 : : {
836 : 2 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
837 : :
838 : 2 : gobject_class->get_property = gcr_secret_exchange_get_property;
839 : 2 : gobject_class->set_property = gcr_secret_exchange_set_property;
840 : 2 : gobject_class->finalize = gcr_secret_exchange_finalize;
841 : :
842 : 2 : klass->generate_exchange_key = gcr_secret_exchange_default_generate_exchange_key;
843 : 2 : klass->derive_transport_key = gcr_secret_exchange_default_derive_transport_key;
844 : 2 : klass->decrypt_transport_data = gcr_secret_exchange_default_decrypt_transport_data;
845 : 2 : klass->encrypt_transport_data = gcr_secret_exchange_default_encrypt_transport_data;
846 : :
847 : 2 : egg_libgcrypt_initialize ();
848 : :
849 : : /**
850 : : * GcrSecretExchange:protocol:
851 : : *
852 : : * The protocol being used for the exchange.
853 : : *
854 : : * Will be %NULL if no protocol was specified when creating this object,
855 : : * and either [method@SecretExchange.begin] or [method@SecretExchange.receive]
856 : : * have not been called successfully.
857 : : */
858 : 2 : g_object_class_install_property (gobject_class, PROP_PROTOCOL,
859 : : g_param_spec_string ("protocol", "Protocol", "Exchange protocol",
860 : : GCR_SECRET_EXCHANGE_PROTOCOL_1,
861 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
862 : 2 : }
|