1 /* libsecret - GLib wrapper for Secret Service
3 * Copyright 2011 Collabora Ltd.
4 * Copyright 2012 Red Hat Inc.
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 published
8 * by the Free Software Foundation; either version 2.1 of the licence or (at
9 * your option) any later version.
11 * See the included COPYING file for more information.
13 * Author: Stef Walter <stefw@gnome.org>
18 #include "secret-private.h"
21 #include "egg/egg-dh.h"
22 #include "egg/egg-hkdf.h"
23 #include "egg/egg-libgcrypt.h"
26 #include "egg/egg-hex.h"
27 #include "egg/egg-secure-memory.h"
29 #include <glib/gi18n-lib.h>
31 EGG_SECURE_DECLARE (secret_session);
33 #define ALGORITHMS_AES "dh-ietf1024-sha256-aes128-cbc-pkcs7"
34 #define ALGORITHMS_PLAIN "plain"
36 struct _SecretSession {
38 const gchar *algorithms;
49 _secret_session_free (gpointer data)
51 SecretSession *session = data;
56 g_free (session->path);
58 gcry_mpi_release (session->publi);
59 gcry_mpi_release (session->privat);
60 gcry_mpi_release (session->prime);
62 egg_secure_free (session->key);
69 request_open_session_aes (SecretSession *session)
73 unsigned char *buffer;
77 g_assert (session->prime == NULL);
78 g_assert (session->privat == NULL);
79 g_assert (session->publi == NULL);
81 egg_libgcrypt_initialize ();
83 /* Initialize our local parameters and values */
84 if (!egg_dh_default_params ("ietf-ike-grp-modp-1024",
85 &session->prime, &base))
86 g_return_val_if_reached (NULL);
89 g_printerr ("\n lib prime: ");
90 gcry_mpi_dump (session->prime);
91 g_printerr ("\n lib base: ");
96 if (!egg_dh_gen_pair (session->prime, base, 0,
97 &session->publi, &session->privat))
98 g_return_val_if_reached (NULL);
99 gcry_mpi_release (base);
101 gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, session->publi);
102 g_return_val_if_fail (gcry == 0, NULL);
103 argument = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
104 buffer, n_buffer, TRUE,
107 return g_variant_new ("(sv)", ALGORITHMS_AES, argument);
111 response_open_session_aes (SecretSession *session,
114 gconstpointer buffer;
123 sig = g_variant_get_type_string (response);
124 g_return_val_if_fail (sig != NULL, FALSE);
126 if (!g_str_equal (sig, "(vo)")) {
127 g_warning ("invalid OpenSession() response from daemon with signature: %s", sig);
131 g_assert (session->path == NULL);
132 g_variant_get (response, "(vo)", &argument, &session->path);
134 buffer = g_variant_get_fixed_array (argument, &n_buffer, sizeof (guchar));
135 gcry = gcry_mpi_scan (&peer, GCRYMPI_FMT_USG, buffer, n_buffer, NULL);
136 g_return_val_if_fail (gcry == 0, FALSE);
137 g_variant_unref (argument);
140 g_printerr (" lib publi: ");
141 gcry_mpi_dump (session->publi);
142 g_printerr ("\n lib peer: ");
143 gcry_mpi_dump (peer);
147 ikm = egg_dh_gen_secret (peer, session->privat, session->prime, &n_ikm);
148 gcry_mpi_release (peer);
151 g_printerr (" lib ikm: %s\n", egg_hex_encode (ikm, n_ikm));
155 g_warning ("couldn't negotiate a valid AES session key");
156 g_free (session->path);
157 session->path = NULL;
162 session->key = egg_secure_alloc (session->n_key);
163 if (!egg_hkdf_perform ("sha256", ikm, n_ikm, NULL, 0, NULL, 0,
164 session->key, session->n_key))
165 g_return_val_if_reached (FALSE);
166 egg_secure_free (ikm);
168 session->algorithms = ALGORITHMS_AES;
172 #endif /* WITH_GCRYPT */
175 request_open_session_plain (SecretSession *session)
177 GVariant *argument = g_variant_new_string ("");
178 return g_variant_new ("(sv)", "plain", argument);
182 response_open_session_plain (SecretSession *session,
188 sig = g_variant_get_type_string (response);
189 g_return_val_if_fail (sig != NULL, FALSE);
191 if (!g_str_equal (sig, "(vo)")) {
192 g_warning ("invalid OpenSession() response from daemon with signature: %s",
193 g_variant_get_type_string (response));
197 g_assert (session->path == NULL);
198 g_variant_get (response, "(vo)", &argument, &session->path);
199 g_variant_unref (argument);
201 g_assert (session->key == NULL);
202 g_assert (session->n_key == 0);
204 session->algorithms = ALGORITHMS_PLAIN;
209 GCancellable *cancellable;
210 SecretSession *session;
211 } OpenSessionClosure;
214 open_session_closure_free (gpointer data)
216 OpenSessionClosure *closure = data;
218 g_clear_object (&closure->cancellable);
219 _secret_session_free (closure->session);
224 on_service_open_session_plain (GObject *source,
225 GAsyncResult *result,
228 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
229 OpenSessionClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
230 SecretService *service = SECRET_SERVICE (source);
231 GError *error = NULL;
234 response = g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error);
236 /* A successful response, decode it */
237 if (response != NULL) {
238 if (response_open_session_plain (closure->session, response)) {
239 _secret_service_take_session (service, closure->session);
240 closure->session = NULL;
243 g_simple_async_result_set_error (res, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
244 _("Couldn't communicate with the secret storage"));
247 g_simple_async_result_complete (res);
248 g_variant_unref (response);
251 g_simple_async_result_take_error (res, error);
252 g_simple_async_result_complete (res);
255 g_object_unref (res);
261 on_service_open_session_aes (GObject *source,
262 GAsyncResult *result,
265 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
266 OpenSessionClosure * closure = g_simple_async_result_get_op_res_gpointer (res);
267 SecretService *service = SECRET_SERVICE (source);
268 GError *error = NULL;
271 response = g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error);
273 /* A successful response, decode it */
274 if (response != NULL) {
275 if (response_open_session_aes (closure->session, response)) {
276 _secret_service_take_session (service, closure->session);
277 closure->session = NULL;
280 g_simple_async_result_set_error (res, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
281 _("Couldn't communicate with the secret storage"));
284 g_simple_async_result_complete (res);
285 g_variant_unref (response);
288 /* AES session not supported, request a plain session */
289 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED)) {
290 g_dbus_proxy_call (G_DBUS_PROXY (source), "OpenSession",
291 request_open_session_plain (closure->session),
292 G_DBUS_CALL_FLAGS_NONE, -1,
293 closure->cancellable, on_service_open_session_plain,
295 g_error_free (error);
297 /* Other errors result in a failure */
299 g_simple_async_result_take_error (res, error);
300 g_simple_async_result_complete (res);
304 g_object_unref (res);
307 #endif /* WITH_GCRYPT */
311 _secret_session_open (SecretService *service,
312 GCancellable *cancellable,
313 GAsyncReadyCallback callback,
316 GSimpleAsyncResult *res;
317 OpenSessionClosure *closure;
319 res = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
320 _secret_session_open);
321 closure = g_new (OpenSessionClosure, 1);
322 closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable;
323 closure->session = g_new0 (SecretSession, 1);
324 g_simple_async_result_set_op_res_gpointer (res, closure, open_session_closure_free);
326 g_dbus_proxy_call (G_DBUS_PROXY (service), "OpenSession",
328 request_open_session_aes (closure->session),
329 G_DBUS_CALL_FLAGS_NONE, -1,
330 cancellable, on_service_open_session_aes,
332 request_open_session_plain (closure->session),
333 G_DBUS_CALL_FLAGS_NONE, -1,
334 cancellable, on_service_open_session_plain,
338 g_object_unref (res);
342 _secret_session_open_finish (GAsyncResult *result,
345 if (_secret_util_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
354 pkcs7_unpad_bytes_in_place (guchar *padded,
362 n_pad = padded[*n_padded - 1];
364 /* Validate the padding */
365 if (n_pad == 0 || n_pad > 16)
367 if (n_pad > *n_padded)
369 for (i = *n_padded - n_pad; i < *n_padded; ++i) {
370 if (padded[i] != n_pad)
374 /* The last bit of data */
377 /* Null teriminate as a courtesy */
378 padded[*n_padded] = 0;
384 service_decode_aes_secret (SecretSession *session,
389 const gchar *content_type)
391 gcry_cipher_hd_t cih;
398 g_message ("received an encrypted secret structure with invalid parameter");
402 if (n_value == 0 || n_value % 16 != 0) {
403 g_message ("received an encrypted secret structure with bad secret length");
407 gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
409 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
414 g_printerr (" lib iv: %s\n", egg_hex_encode (param, n_param));
417 gcry = gcry_cipher_setiv (cih, param, n_param);
418 g_return_val_if_fail (gcry == 0, NULL);
421 g_printerr (" lib key: %s\n", egg_hex_encode (session->key, session->n_key));
424 gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
425 g_return_val_if_fail (gcry == 0, NULL);
427 /* Copy the memory buffer */
429 padded = egg_secure_alloc (n_padded);
430 memcpy (padded, value, n_padded);
432 /* Perform the decryption */
433 for (pos = 0; pos < n_padded; pos += 16) {
434 gcry = gcry_cipher_decrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
435 g_return_val_if_fail (gcry == 0, FALSE);
438 gcry_cipher_close (cih);
440 /* Unpad the resulting value */
441 if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) {
442 egg_secure_clear (padded, n_padded);
443 egg_secure_free (padded);
444 g_message ("received an invalid or unencryptable secret");
448 return secret_value_new_full ((gchar *)padded, n_padded, content_type, egg_secure_free);
451 #endif /* WITH_GCRYPT */
454 service_decode_plain_secret (SecretSession *session,
459 const gchar *content_type)
462 g_message ("received a plain secret structure with invalid parameter");
466 return secret_value_new (value, n_value, content_type);
470 _secret_session_decode_secret (SecretSession *session,
483 g_return_val_if_fail (session != NULL, NULL);
484 g_return_val_if_fail (encoded != NULL, NULL);
486 /* Parsing (oayays) */
487 g_variant_get_child (encoded, 0, "o", &session_path);
489 if (session_path == NULL || !g_str_equal (session_path, session->path)) {
490 g_message ("received a secret encoded with wrong session: %s != %s",
491 session_path, session->path);
492 g_free (session_path);
496 vparam = g_variant_get_child_value (encoded, 1);
497 param = g_variant_get_fixed_array (vparam, &n_param, sizeof (guchar));
498 vvalue = g_variant_get_child_value (encoded, 2);
499 value = g_variant_get_fixed_array (vvalue, &n_value, sizeof (guchar));
500 g_variant_get_child (encoded, 3, "s", &content_type);
503 if (session->key != NULL)
504 result = service_decode_aes_secret (session, param, n_param,
505 value, n_value, content_type);
508 result = service_decode_plain_secret (session, param, n_param,
509 value, n_value, content_type);
511 g_variant_unref (vparam);
512 g_variant_unref (vvalue);
513 g_free (content_type);
514 g_free (session_path);
522 pkcs7_pad_bytes_in_secure_memory (gconstpointer secret,
530 *n_padded = ((length + 16) / 16) * 16;
531 g_assert (length < *n_padded);
532 g_assert (*n_padded > 0);
533 n_pad = *n_padded - length;
534 g_assert (n_pad > 0 && n_pad <= 16);
535 padded = egg_secure_alloc (*n_padded);
536 memcpy (padded, secret, length);
537 memset (padded + length, n_pad, n_pad);
542 service_encode_aes_secret (SecretSession *session,
544 GVariantBuilder *builder)
546 gcry_cipher_hd_t cih;
551 gconstpointer secret;
555 g_variant_builder_add (builder, "o", session->path);
557 /* Create the cipher */
558 gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
560 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
564 secret = secret_value_get (value, &n_secret);
566 /* Perform the encoding here */
567 padded = pkcs7_pad_bytes_in_secure_memory (secret, n_secret, &n_padded);
568 g_assert (padded != NULL);
572 gcry_create_nonce (iv, 16);
573 gcry = gcry_cipher_setiv (cih, iv, 16);
574 g_return_val_if_fail (gcry == 0, FALSE);
577 gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
578 g_return_val_if_fail (gcry == 0, FALSE);
580 /* Perform the encryption */
581 for (pos = 0; pos < n_padded; pos += 16) {
582 gcry = gcry_cipher_encrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
583 g_return_val_if_fail (gcry == 0, FALSE);
586 gcry_cipher_close (cih);
588 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), iv, 16, TRUE, g_free, iv);
589 g_variant_builder_add_value (builder, child);
591 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), padded, n_padded, TRUE, egg_secure_free, padded);
592 g_variant_builder_add_value (builder, child);
594 g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
598 #endif /* WITH_GCRYPT */
601 service_encode_plain_secret (SecretSession *session,
603 GVariantBuilder *builder)
605 gconstpointer secret;
609 g_variant_builder_add (builder, "o", session->path);
611 secret = secret_value_get (value, &n_secret);
613 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), "", 0, TRUE, NULL, NULL);
614 g_variant_builder_add_value (builder, child);
615 g_variant_unref (child);
617 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), secret, n_secret, TRUE,
618 secret_value_unref, secret_value_ref (value));
619 g_variant_builder_add_value (builder, child);
620 g_variant_unref (child);
622 g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
627 _secret_session_encode_secret (SecretSession *session,
630 GVariantBuilder *builder;
631 GVariant *result = NULL;
635 g_return_val_if_fail (session != NULL, NULL);
636 g_return_val_if_fail (value != NULL, NULL);
638 type = g_variant_type_new ("(oayays)");
639 builder = g_variant_builder_new (type);
643 ret = service_encode_aes_secret (session, value, builder);
646 ret = service_encode_plain_secret (session, value, builder);
648 result = g_variant_builder_end (builder);
650 g_variant_builder_unref (builder);
651 g_variant_type_free (type);
656 _secret_session_get_algorithms (SecretSession *session)
658 g_return_val_if_fail (session != NULL, NULL);
659 return session->algorithms;
663 _secret_session_get_path (SecretSession *session)
665 g_return_val_if_fail (session != NULL, NULL);
666 return session->path;