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 _secret_util_strip_remote_error (&error);
252 g_simple_async_result_take_error (res, error);
253 g_simple_async_result_complete (res);
256 g_object_unref (res);
262 on_service_open_session_aes (GObject *source,
263 GAsyncResult *result,
266 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
267 OpenSessionClosure * closure = g_simple_async_result_get_op_res_gpointer (res);
268 SecretService *service = SECRET_SERVICE (source);
269 GError *error = NULL;
272 response = g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error);
274 /* A successful response, decode it */
275 if (response != NULL) {
276 if (response_open_session_aes (closure->session, response)) {
277 _secret_service_take_session (service, closure->session);
278 closure->session = NULL;
281 g_simple_async_result_set_error (res, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
282 _("Couldn't communicate with the secret storage"));
285 g_simple_async_result_complete (res);
286 g_variant_unref (response);
289 /* AES session not supported, request a plain session */
290 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED)) {
291 g_dbus_proxy_call (G_DBUS_PROXY (source), "OpenSession",
292 request_open_session_plain (closure->session),
293 G_DBUS_CALL_FLAGS_NONE, -1,
294 closure->cancellable, on_service_open_session_plain,
296 g_error_free (error);
298 /* Other errors result in a failure */
300 _secret_util_strip_remote_error (&error);
301 g_simple_async_result_take_error (res, error);
302 g_simple_async_result_complete (res);
306 g_object_unref (res);
309 #endif /* WITH_GCRYPT */
313 _secret_session_open (SecretService *service,
314 GCancellable *cancellable,
315 GAsyncReadyCallback callback,
318 GSimpleAsyncResult *res;
319 OpenSessionClosure *closure;
321 res = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
322 _secret_session_open);
323 closure = g_new (OpenSessionClosure, 1);
324 closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable;
325 closure->session = g_new0 (SecretSession, 1);
326 g_simple_async_result_set_op_res_gpointer (res, closure, open_session_closure_free);
328 g_dbus_proxy_call (G_DBUS_PROXY (service), "OpenSession",
330 request_open_session_aes (closure->session),
331 G_DBUS_CALL_FLAGS_NONE, -1,
332 cancellable, on_service_open_session_aes,
334 request_open_session_plain (closure->session),
335 G_DBUS_CALL_FLAGS_NONE, -1,
336 cancellable, on_service_open_session_plain,
340 g_object_unref (res);
344 _secret_session_open_finish (GAsyncResult *result,
347 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
356 pkcs7_unpad_bytes_in_place (guchar *padded,
364 n_pad = padded[*n_padded - 1];
366 /* Validate the padding */
367 if (n_pad == 0 || n_pad > 16)
369 if (n_pad > *n_padded)
371 for (i = *n_padded - n_pad; i < *n_padded; ++i) {
372 if (padded[i] != n_pad)
376 /* The last bit of data */
379 /* Null teriminate as a courtesy */
380 padded[*n_padded] = 0;
386 service_decode_aes_secret (SecretSession *session,
391 const gchar *content_type)
393 gcry_cipher_hd_t cih;
400 g_message ("received an encrypted secret structure with invalid parameter");
404 if (n_value == 0 || n_value % 16 != 0) {
405 g_message ("received an encrypted secret structure with bad secret length");
409 gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
411 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
416 g_printerr (" lib iv: %s\n", egg_hex_encode (param, n_param));
419 gcry = gcry_cipher_setiv (cih, param, n_param);
420 g_return_val_if_fail (gcry == 0, NULL);
423 g_printerr (" lib key: %s\n", egg_hex_encode (session->key, session->n_key));
426 gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
427 g_return_val_if_fail (gcry == 0, NULL);
429 /* Copy the memory buffer */
431 padded = egg_secure_alloc (n_padded);
432 memcpy (padded, value, n_padded);
434 /* Perform the decryption */
435 for (pos = 0; pos < n_padded; pos += 16) {
436 gcry = gcry_cipher_decrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
437 g_return_val_if_fail (gcry == 0, FALSE);
440 gcry_cipher_close (cih);
442 /* Unpad the resulting value */
443 if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) {
444 egg_secure_clear (padded, n_padded);
445 egg_secure_free (padded);
446 g_message ("received an invalid or unencryptable secret");
450 return secret_value_new_full ((gchar *)padded, n_padded, content_type, egg_secure_free);
453 #endif /* WITH_GCRYPT */
456 service_decode_plain_secret (SecretSession *session,
461 const gchar *content_type)
464 g_message ("received a plain secret structure with invalid parameter");
468 return secret_value_new (value, n_value, content_type);
472 _secret_session_decode_secret (SecretSession *session,
485 g_return_val_if_fail (session != NULL, NULL);
486 g_return_val_if_fail (encoded != NULL, NULL);
488 /* Parsing (oayays) */
489 g_variant_get_child (encoded, 0, "o", &session_path);
491 if (session_path == NULL || !g_str_equal (session_path, session->path)) {
492 g_message ("received a secret encoded with wrong session: %s != %s",
493 session_path, session->path);
494 g_free (session_path);
498 vparam = g_variant_get_child_value (encoded, 1);
499 param = g_variant_get_fixed_array (vparam, &n_param, sizeof (guchar));
500 vvalue = g_variant_get_child_value (encoded, 2);
501 value = g_variant_get_fixed_array (vvalue, &n_value, sizeof (guchar));
502 g_variant_get_child (encoded, 3, "s", &content_type);
505 if (session->key != NULL)
506 result = service_decode_aes_secret (session, param, n_param,
507 value, n_value, content_type);
510 result = service_decode_plain_secret (session, param, n_param,
511 value, n_value, content_type);
513 g_variant_unref (vparam);
514 g_variant_unref (vvalue);
515 g_free (content_type);
516 g_free (session_path);
524 pkcs7_pad_bytes_in_secure_memory (gconstpointer secret,
532 *n_padded = ((length + 16) / 16) * 16;
533 g_assert (length < *n_padded);
534 g_assert (*n_padded > 0);
535 n_pad = *n_padded - length;
536 g_assert (n_pad > 0 && n_pad <= 16);
537 padded = egg_secure_alloc (*n_padded);
538 memcpy (padded, secret, length);
539 memset (padded + length, n_pad, n_pad);
544 service_encode_aes_secret (SecretSession *session,
546 GVariantBuilder *builder)
548 gcry_cipher_hd_t cih;
553 gconstpointer secret;
557 g_variant_builder_add (builder, "o", session->path);
559 /* Create the cipher */
560 gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
562 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
566 secret = secret_value_get (value, &n_secret);
568 /* Perform the encoding here */
569 padded = pkcs7_pad_bytes_in_secure_memory (secret, n_secret, &n_padded);
570 g_assert (padded != NULL);
574 gcry_create_nonce (iv, 16);
575 gcry = gcry_cipher_setiv (cih, iv, 16);
576 g_return_val_if_fail (gcry == 0, FALSE);
579 gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
580 g_return_val_if_fail (gcry == 0, FALSE);
582 /* Perform the encryption */
583 for (pos = 0; pos < n_padded; pos += 16) {
584 gcry = gcry_cipher_encrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
585 g_return_val_if_fail (gcry == 0, FALSE);
588 gcry_cipher_close (cih);
590 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), iv, 16, TRUE, g_free, iv);
591 g_variant_builder_add_value (builder, child);
593 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), padded, n_padded, TRUE, egg_secure_free, padded);
594 g_variant_builder_add_value (builder, child);
596 g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
600 #endif /* WITH_GCRYPT */
603 service_encode_plain_secret (SecretSession *session,
605 GVariantBuilder *builder)
607 gconstpointer secret;
611 g_variant_builder_add (builder, "o", session->path);
613 secret = secret_value_get (value, &n_secret);
615 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), "", 0, TRUE, NULL, NULL);
616 g_variant_builder_add_value (builder, child);
617 g_variant_unref (child);
619 child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), secret, n_secret, TRUE,
620 secret_value_unref, secret_value_ref (value));
621 g_variant_builder_add_value (builder, child);
622 g_variant_unref (child);
624 g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
629 _secret_session_encode_secret (SecretSession *session,
632 GVariantBuilder *builder;
633 GVariant *result = NULL;
637 g_return_val_if_fail (session != NULL, NULL);
638 g_return_val_if_fail (value != NULL, NULL);
640 type = g_variant_type_new ("(oayays)");
641 builder = g_variant_builder_new (type);
645 ret = service_encode_aes_secret (session, value, builder);
648 ret = service_encode_plain_secret (session, value, builder);
650 result = g_variant_builder_end (builder);
652 g_variant_builder_unref (builder);
653 g_variant_type_free (type);
658 _secret_session_get_algorithms (SecretSession *session)
660 g_return_val_if_fail (session != NULL, NULL);
661 return session->algorithms;
665 _secret_session_get_path (SecretSession *session)
667 g_return_val_if_fail (session != NULL, NULL);
668 return session->path;