From 28486191b2d2cf1599cd3c051b304fac927e24cf Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Sun, 10 Sep 2023 11:42:06 +0900 Subject: [PATCH] Support GnuTLS as an alternative crypto backend This turns the `-Dgcrypt` build time option into a more generic `-Dcrypto` option, which enables user to choose which cryptographic library to link with. It currently supports libgcrypt (`libgcrypt`) and GnuTLS (`gnutls`); for the latter, GnuTLS 3.8.2 is the minimum required version. Signed-off-by: Daiki Ueno --- egg/egg-dh-gnutls.c | 261 ++++++++++++++++ egg/egg-dh-libgcrypt.c | 278 ++++++++++++++++++ egg/egg-dh.c | 272 +---------------- egg/egg-dh.h | 11 + egg/egg-hkdf-gnutls.c | 110 +++++++ egg/{egg-hkdf.c => egg-hkdf-libgcrypt.c} | 9 +- egg/egg-keyring1-gnutls.c | 156 ++++++++++ ...gg-keyring1.c => egg-keyring1-libgcrypt.c} | 0 egg/meson.build | 24 +- egg/test-dh.c | 11 - libsecret/meson.build | 8 +- libsecret/secret-backend.c | 6 +- libsecret/secret-file-collection.c | 8 +- libsecret/secret-session.c | 182 +++++++++++- libsecret/test-session.c | 2 +- meson.build | 42 ++- meson_options.txt | 2 +- tool/meson.build | 2 +- 18 files changed, 1064 insertions(+), 320 deletions(-) create mode 100644 egg/egg-dh-gnutls.c create mode 100644 egg/egg-dh-libgcrypt.c create mode 100644 egg/egg-hkdf-gnutls.c rename egg/{egg-hkdf.c => egg-hkdf-libgcrypt.c} (91%) create mode 100644 egg/egg-keyring1-gnutls.c rename egg/{egg-keyring1.c => egg-keyring1-libgcrypt.c} (100%) diff --git a/egg/egg-dh-gnutls.c b/egg/egg-dh-gnutls.c new file mode 100644 index 0000000..1273d23 --- /dev/null +++ b/egg/egg-dh-gnutls.c @@ -0,0 +1,261 @@ +/* + * libsecret + * + * Copyright (C) 2023 Daiki Ueno + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received copies of the GNU General Public License and + * the GNU Lesser General Public License along with this program. If + * not, see http://www.gnu.org/licenses/. + * + * Author: Daiki Ueno + */ + +#include "config.h" + +#include "egg-dh.h" + +/* Enabling this is a complete security compromise */ +#define DEBUG_DH_SECRET 0 + +#include +#include + +struct egg_dh_params { + gnutls_dh_params_t inner; + guint bits; +}; + +struct egg_dh_pubkey { + gnutls_pubkey_t inner; +}; + +struct egg_dh_privkey { + gnutls_privkey_t inner; +}; + +egg_dh_params * +egg_dh_default_params (const gchar *name) +{ + const egg_dh_group *group; + egg_dh_params *params; + + g_return_val_if_fail (name, NULL); + + for (group = egg_dh_groups; group->name; ++group) { + if (g_str_equal (group->name, name)) { + gnutls_dh_params_t inner; + gnutls_datum_t prime, generator; + int ret; + + ret = gnutls_dh_params_init (&inner); + if (ret < 0) + return NULL; + + prime.data = (void *)group->prime; + prime.size = group->n_prime; + generator.data = (void *)group->base; + generator.size = group->n_base; + + ret = gnutls_dh_params_import_raw (inner, + &prime, + &generator); + if (ret < 0) { + gnutls_dh_params_deinit (inner); + return NULL; + } + + params = g_new (struct egg_dh_params, 1); + if (!params) { + gnutls_dh_params_deinit (inner); + return NULL; + } + params->inner = g_steal_pointer (&inner); + params->bits = group->bits; + return params; + } + } + + return NULL; +} + +gboolean +egg_dh_gen_pair (egg_dh_params *params, guint bits, + egg_dh_pubkey **pub, egg_dh_privkey **priv) +{ + gnutls_pubkey_t pub_inner = NULL; + gnutls_privkey_t priv_inner = NULL; + egg_dh_pubkey *pub_outer = NULL; + egg_dh_privkey *priv_outer = NULL; + gnutls_keygen_data_st data; + gboolean ok = FALSE; + int ret; + + g_return_val_if_fail (params, FALSE); + g_return_val_if_fail (pub, FALSE); + g_return_val_if_fail (priv, FALSE); + + if (bits == 0) + bits = params->bits; + else if (bits > params->bits) + g_return_val_if_reached (FALSE); + + ret = gnutls_privkey_init (&priv_inner); + if (ret < 0) + goto out; + + data.type = GNUTLS_KEYGEN_DH; + data.data = (void *)params->inner; + + ret = gnutls_privkey_generate2 (priv_inner, GNUTLS_PK_DH, bits, 0, + &data, 1); + if (ret < 0) + goto out; + + ret = gnutls_pubkey_init (&pub_inner); + if (ret < 0) + goto out; + + ret = gnutls_pubkey_import_privkey (pub_inner, priv_inner, 0, 0); + if (ret < 0) + goto out; + + pub_outer = g_new0 (struct egg_dh_pubkey, 1); + if (!pub_outer) + goto out; + pub_outer->inner = g_steal_pointer (&pub_inner); + + priv_outer = g_new0 (struct egg_dh_privkey, 1); + if (!priv_outer) + goto out; + priv_outer->inner = g_steal_pointer (&priv_inner); + + *pub = g_steal_pointer (&pub_outer); + *priv = g_steal_pointer (&priv_outer); + + ok = TRUE; + + out: + if (priv_inner) + gnutls_privkey_deinit (priv_inner); + if (pub_inner) + gnutls_pubkey_deinit (pub_inner); + + egg_dh_privkey_free (priv_outer); + egg_dh_pubkey_free (pub_outer); + + return ok; +} + +GBytes * +egg_dh_gen_secret (egg_dh_pubkey *peer, egg_dh_privkey *priv, + egg_dh_params *params) +{ + int ret; + gnutls_datum_t k; +#if DEBUG_DH_SECRET + gnutls_datum_t h; +#endif + + g_return_val_if_fail (peer, NULL); + g_return_val_if_fail (priv, NULL); + g_return_val_if_fail (params, NULL); + + ret = gnutls_privkey_derive_secret (priv->inner, peer->inner, + NULL, &k, 0); + if (ret < 0) + return NULL; + +#if DEBUG_DH_SECRET + ret = gnutls_hex_encode2 (&k, &h); + g_assert (ret >= 0); + g_printerr ("DH SECRET: %s\n", h.data); + gnutls_free (h.data); +#endif + + return g_bytes_new_with_free_func (k.data, k.size, + (GDestroyNotify)gnutls_free, + k.data); +} + +void +egg_dh_params_free (egg_dh_params *params) +{ + gnutls_dh_params_deinit (params->inner); + g_free (params); +} + +void +egg_dh_pubkey_free (egg_dh_pubkey *pubkey) +{ + if (!pubkey) + return; + if (pubkey->inner) + gnutls_pubkey_deinit (pubkey->inner); + g_free (pubkey); +} + +void +egg_dh_privkey_free (egg_dh_privkey *privkey) +{ + if (!privkey) + return; + if (privkey->inner) + gnutls_privkey_deinit (privkey->inner); + g_free (privkey); +} + +GBytes * +egg_dh_pubkey_export (const egg_dh_pubkey *pubkey) +{ + gnutls_datum_t data; + int ret; + + ret = gnutls_pubkey_export_dh_raw (pubkey->inner, NULL, &data, 0); + if (ret < 0) + return NULL; + + return g_bytes_new_with_free_func (data.data, data.size, + (GDestroyNotify)gnutls_free, + data.data); +} + +egg_dh_pubkey * +egg_dh_pubkey_new_from_bytes (const egg_dh_params *params, GBytes *bytes) +{ + egg_dh_pubkey *pub; + gnutls_pubkey_t inner; + gnutls_datum_t data; + int ret; + + ret = gnutls_pubkey_init (&inner); + if (ret < 0) + return NULL; + + data.data = (void *)g_bytes_get_data (bytes, NULL); + data.size = g_bytes_get_size (bytes); + + ret = gnutls_pubkey_import_dh_raw (inner, params->inner, &data); + if (ret < 0) { + gnutls_pubkey_deinit (inner); + return NULL; + } + + pub = g_new (struct egg_dh_pubkey, 1); + if (!pub) { + gnutls_pubkey_deinit (inner); + return NULL; + } + + pub->inner = g_steal_pointer (&inner); + return pub; +} diff --git a/egg/egg-dh-libgcrypt.c b/egg/egg-dh-libgcrypt.c new file mode 100644 index 0000000..8cb78b4 --- /dev/null +++ b/egg/egg-dh-libgcrypt.c @@ -0,0 +1,278 @@ +/* + * gnome-keyring + * + * Copyright (C) 2009 Stefan Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received copies of the GNU General Public License and + * the GNU Lesser General Public License along with this program. If + * not, see http://www.gnu.org/licenses/. + * + * Author: Stef Walter + */ + +#include "config.h" + +#include "egg-dh.h" + +#include +#include "egg-secure-memory.h" + +/* Enabling this is a complete security compromise */ +#define DEBUG_DH_SECRET 0 + +EGG_SECURE_DECLARE (dh); + +struct egg_dh_params { + gcry_mpi_t prime; + gcry_mpi_t base; +}; + +struct egg_dh_pubkey { + gcry_mpi_t inner; +}; + +struct egg_dh_privkey { + gcry_mpi_t inner; +}; + +egg_dh_params * +egg_dh_default_params (const gchar *name) +{ + const egg_dh_group *group; + gcry_error_t gcry; + gcry_mpi_t prime = NULL, base = NULL; + egg_dh_params *params = NULL; + + g_return_val_if_fail (name, NULL); + + for (group = egg_dh_groups; group->name; ++group) + if (g_str_equal (group->name, name)) + break; + if (!group->name) + return NULL; + + gcry = gcry_mpi_scan (&prime, GCRYMPI_FMT_USG, + group->prime, group->n_prime, NULL); + g_return_val_if_fail (gcry == 0, NULL); + g_return_val_if_fail (gcry_mpi_get_nbits (prime) == group->bits, NULL); + + gcry = gcry_mpi_scan (&base, GCRYMPI_FMT_USG, + group->base, group->n_base, NULL); + g_return_val_if_fail (gcry == 0, NULL); + + params = g_new (struct egg_dh_params, 1); + if (!params) + goto error; + + params->prime = g_steal_pointer (&prime); + params->base = g_steal_pointer (&base); + + error: + gcry_mpi_release (prime); + gcry_mpi_release (base); + return params; +} + +gboolean +egg_dh_gen_pair (egg_dh_params *params, guint bits, + egg_dh_pubkey **pub, egg_dh_privkey **priv) +{ + guint pbits; + gcry_mpi_t pub_inner = NULL, priv_inner = NULL; + + g_return_val_if_fail (params, FALSE); + g_return_val_if_fail (pub, FALSE); + g_return_val_if_fail (priv, FALSE); + + *pub = NULL; + *priv = NULL; + + pbits = gcry_mpi_get_nbits (params->prime); + g_return_val_if_fail (pbits > 1, FALSE); + + if (bits == 0) { + bits = pbits; + } else if (bits > pbits) { + g_return_val_if_reached (FALSE); + } + + /* + * Generate a strong random number of bits, and not zero. + * gcry_mpi_randomize bumps up to the next byte. Since we + * need to have a value less than half of prime, we make sure + * we bump down. + */ + priv_inner = gcry_mpi_snew (bits); + g_return_val_if_fail (priv_inner, FALSE); + while (gcry_mpi_cmp_ui (priv_inner, 0) == 0) + gcry_mpi_randomize (priv_inner, bits, GCRY_STRONG_RANDOM); + + /* Secret key value must be less than half of p */ + if (gcry_mpi_get_nbits (priv_inner) > bits) + gcry_mpi_clear_highbit (priv_inner, bits); + if (gcry_mpi_get_nbits (priv_inner) > pbits - 1) + gcry_mpi_clear_highbit (priv_inner, pbits - 1); + g_assert (gcry_mpi_cmp (params->prime, priv_inner) > 0); + + pub_inner = gcry_mpi_new (gcry_mpi_get_nbits (priv_inner)); + if (!pub_inner) + goto error; + gcry_mpi_powm (pub_inner, params->base, priv_inner, params->prime); + + *priv = g_new0 (struct egg_dh_privkey, 1); + if (!*priv) + goto error; + (*priv)->inner = g_steal_pointer (&priv_inner); + + *pub = g_new0 (struct egg_dh_pubkey, 1); + if (!*pub) + goto error; + (*pub)->inner = g_steal_pointer (&pub_inner); + + return TRUE; + error: + egg_dh_privkey_free (*priv); + egg_dh_pubkey_free (*pub); + + gcry_mpi_release (priv_inner); + gcry_mpi_release (pub_inner); + + g_return_val_if_reached (FALSE); +} + +GBytes * +egg_dh_gen_secret (egg_dh_pubkey *peer, egg_dh_privkey *priv, + egg_dh_params *params) +{ + gcry_error_t gcry; + guchar *value; + gsize n_prime; + gsize n_value; + gcry_mpi_t k; + gint bits; + + g_return_val_if_fail (peer, NULL); + g_return_val_if_fail (priv, NULL); + g_return_val_if_fail (params, NULL); + + bits = gcry_mpi_get_nbits (params->prime); + g_return_val_if_fail (bits >= 0, NULL); + + k = gcry_mpi_snew (bits); + g_return_val_if_fail (k, NULL); + gcry_mpi_powm (k, peer->inner, priv->inner, params->prime); + + /* Write out the secret */ + gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_prime, params->prime); + g_return_val_if_fail (gcry == 0, NULL); + + value = egg_secure_alloc (n_prime); + if (!value) + return NULL; + + gcry = gcry_mpi_print (GCRYMPI_FMT_USG, value, n_prime, &n_value, k); + g_return_val_if_fail (gcry == 0, NULL); + + /* Pad the secret with zero bytes to match length of prime in bytes. */ + if (n_value < n_prime) { + memmove (value + (n_prime - n_value), value, n_value); + memset (value, 0, (n_prime - n_value)); + } + +#if DEBUG_DH_SECRET + g_printerr ("DH SECRET: "); + gcry_mpi_dump (k); +#endif + gcry_mpi_release (k); + +#if DEBUG_DH_SECRET + gcry_mpi_scan (&k, GCRYMPI_FMT_USG, value, n_prime, NULL); + g_printerr ("RAW SECRET: "); + gcry_mpi_dump (k); + gcry_mpi_release (k); +#endif + + return g_bytes_new_with_free_func (value, n_prime, + (GDestroyNotify)egg_secure_free, + value); +} + +void +egg_dh_params_free (egg_dh_params *params) +{ + if (!params) + return; + gcry_mpi_release (params->prime); + gcry_mpi_release (params->base); + g_free (params); +} + +void +egg_dh_pubkey_free (egg_dh_pubkey *pubkey) +{ + if (!pubkey) + return; + if (pubkey->inner) + gcry_mpi_release (pubkey->inner); + g_free (pubkey); +} + +void +egg_dh_privkey_free (egg_dh_privkey *privkey) +{ + if (!privkey) + return; + if (privkey->inner) + gcry_mpi_release (privkey->inner); + g_free (privkey); +} + +GBytes * +egg_dh_pubkey_export (const egg_dh_pubkey *pubkey) +{ + gcry_error_t gcry; + unsigned char *buffer; + size_t n_buffer; + + gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, + pubkey->inner); + g_return_val_if_fail (gcry == 0, NULL); + + return g_bytes_new_with_free_func (buffer, n_buffer, + gcry_free, buffer); +} + +egg_dh_pubkey * +egg_dh_pubkey_new_from_bytes (const egg_dh_params *params, + GBytes *bytes) +{ + gcry_error_t gcry; + gcry_mpi_t inner; + egg_dh_pubkey *pub; + + gcry = gcry_mpi_scan (&inner, GCRYMPI_FMT_USG, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes), + NULL); + if (gcry != 0) + return NULL; + + pub = g_new (struct egg_dh_pubkey, 1); + if (!pub) { + gcry_mpi_release (inner); + return NULL; + } + + pub->inner = inner; + return pub; +} diff --git a/egg/egg-dh.c b/egg/egg-dh.c index d3d66b3..bba5bf4 100644 --- a/egg/egg-dh.c +++ b/egg/egg-dh.c @@ -13,10 +13,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA + * You should have received copies of the GNU General Public License and + * the GNU Lesser General Public License along with this program. If + * not, see http://www.gnu.org/licenses/. * * Author: Stef Walter */ @@ -24,23 +23,6 @@ #include "config.h" #include "egg-dh.h" -#include "egg-secure-memory.h" - -#include - -/* Enabling this is a complete security compromise */ -#define DEBUG_DH_SECRET 0 - -EGG_SECURE_DECLARE (dh); - -typedef struct _DHGroup { - const gchar *name; - guint bits; - const guchar *prime; - gsize n_prime; - const guchar base[1]; - gsize n_base; -} DHGroup; static const guchar dh_group_768_prime[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, @@ -173,7 +155,7 @@ static const guchar dh_group_8192_prime[] = { 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; -static const DHGroup dh_groups[] = { +const egg_dh_group egg_dh_groups[] = { { "ietf-ike-grp-modp-768", 768, dh_group_768_prime, G_N_ELEMENTS (dh_group_768_prime), @@ -214,24 +196,11 @@ static const DHGroup dh_groups[] = { } }; -struct egg_dh_params { - gcry_mpi_t prime; - gcry_mpi_t base; -}; - -struct egg_dh_pubkey { - gcry_mpi_t inner; -}; - -struct egg_dh_privkey { - gcry_mpi_t inner; -}; - gboolean egg_dh_default_params_raw (const gchar *name, gconstpointer *prime, gsize *n_prime, gconstpointer *base, gsize *n_base) { - const DHGroup *group; + const egg_dh_group *group; g_return_val_if_fail (name, FALSE); g_return_val_if_fail (prime, FALSE); @@ -239,7 +208,7 @@ egg_dh_default_params_raw (const gchar *name, gconstpointer *prime, g_return_val_if_fail (base, FALSE); g_return_val_if_fail (n_base, FALSE); - for (group = dh_groups; group->name; ++group) { + for (group = egg_dh_groups; group->name; ++group) { if (g_str_equal (group->name, name)) { *prime = group->prime; *n_prime = group->n_prime; @@ -251,232 +220,3 @@ egg_dh_default_params_raw (const gchar *name, gconstpointer *prime, return FALSE; } - -egg_dh_params * -egg_dh_default_params (const gchar *name) -{ - const DHGroup *group; - gcry_error_t gcry; - gcry_mpi_t prime = NULL, base = NULL; - egg_dh_params *params = NULL; - - g_return_val_if_fail (name, NULL); - - for (group = dh_groups; group->name; ++group) - if (g_str_equal (group->name, name)) - break; - if (!group->name) - return NULL; - - gcry = gcry_mpi_scan (&prime, GCRYMPI_FMT_USG, - group->prime, group->n_prime, NULL); - g_return_val_if_fail (gcry == 0, NULL); - if (G_UNLIKELY (gcry_mpi_get_nbits (prime) != group->bits)) - goto error; - - gcry = gcry_mpi_scan (&base, GCRYMPI_FMT_USG, - group->base, group->n_base, NULL); - if (gcry != 0) - goto error; - - params = g_new (struct egg_dh_params, 1); - if (!params) - goto error; - - params->prime = g_steal_pointer (&prime); - params->base = g_steal_pointer (&base); - - error: - gcry_mpi_release (prime); - gcry_mpi_release (base); - return params; -} - -gboolean -egg_dh_gen_pair (egg_dh_params *params, guint bits, - egg_dh_pubkey **pub, egg_dh_privkey **priv) -{ - guint pbits; - gcry_mpi_t pub_inner = NULL, priv_inner = NULL; - - g_return_val_if_fail (params, FALSE); - g_return_val_if_fail (pub, FALSE); - g_return_val_if_fail (priv, FALSE); - - *pub = NULL; - *priv = NULL; - - pbits = gcry_mpi_get_nbits (params->prime); - g_return_val_if_fail (pbits > 1, FALSE); - - if (bits == 0) { - bits = pbits; - } else if (bits > pbits) { - g_return_val_if_reached (FALSE); - } - - /* - * Generate a strong random number of bits, and not zero. - * gcry_mpi_randomize bumps up to the next byte. Since we - * need to have a value less than half of prime, we make sure - * we bump down. - */ - priv_inner = gcry_mpi_snew (bits); - g_return_val_if_fail (priv_inner, FALSE); - while (gcry_mpi_cmp_ui (priv_inner, 0) == 0) - gcry_mpi_randomize (priv_inner, bits, GCRY_STRONG_RANDOM); - - /* Secret key value must be less than half of p */ - if (gcry_mpi_get_nbits (priv_inner) > bits) - gcry_mpi_clear_highbit (priv_inner, bits); - if (gcry_mpi_get_nbits (priv_inner) > pbits - 1) - gcry_mpi_clear_highbit (priv_inner, pbits - 1); - g_assert (gcry_mpi_cmp (params->prime, priv_inner) > 0); - - pub_inner = gcry_mpi_new (gcry_mpi_get_nbits (priv_inner)); - if (!pub_inner) - goto error; - gcry_mpi_powm (pub_inner, params->base, priv_inner, params->prime); - - *priv = g_new0 (struct egg_dh_privkey, 1); - if (!*priv) - goto error; - (*priv)->inner = g_steal_pointer (&priv_inner); - - *pub = g_new0 (struct egg_dh_pubkey, 1); - if (!*pub) - goto error; - (*pub)->inner = g_steal_pointer (&pub_inner); - - return TRUE; - error: - egg_dh_privkey_free (*priv); - egg_dh_pubkey_free (*pub); - - gcry_mpi_release (priv_inner); - gcry_mpi_release (pub_inner); - - g_return_val_if_reached (FALSE); -} - -GBytes * -egg_dh_gen_secret (egg_dh_pubkey *peer, egg_dh_privkey *priv, - egg_dh_params *params) -{ - gcry_error_t gcry; - guchar *value; - gsize n_prime; - gsize n_value; - gcry_mpi_t k; - gint bits; - - g_return_val_if_fail (peer, NULL); - g_return_val_if_fail (priv, NULL); - g_return_val_if_fail (params, NULL); - - bits = gcry_mpi_get_nbits (params->prime); - g_return_val_if_fail (bits >= 0, NULL); - - k = gcry_mpi_snew (bits); - g_return_val_if_fail (k, NULL); - gcry_mpi_powm (k, peer->inner, priv->inner, params->prime); - - /* Write out the secret */ - gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_prime, params->prime); - g_return_val_if_fail (gcry == 0, NULL); - value = egg_secure_alloc (n_prime); - gcry = gcry_mpi_print (GCRYMPI_FMT_USG, value, n_prime, &n_value, k); - g_return_val_if_fail (gcry == 0, NULL); - - /* Pad the secret with zero bytes to match length of prime in bytes. */ - if (n_value < n_prime) { - memmove (value + (n_prime - n_value), value, n_value); - memset (value, 0, (n_prime - n_value)); - } - -#if DEBUG_DH_SECRET - g_printerr ("DH SECRET: "); - gcry_mpi_dump (k); -#endif - gcry_mpi_release (k); - -#if DEBUG_DH_SECRET - gcry_mpi_scan (&k, GCRYMPI_FMT_USG, value, n_prime, NULL); - g_printerr ("RAW SECRET: "); - gcry_mpi_dump (k); - gcry_mpi_release (k); -#endif - - return g_bytes_new_with_free_func (value, n_prime, - (GDestroyNotify)egg_secure_free, - value); -} - -void -egg_dh_params_free (egg_dh_params *params) -{ - if (!params) - return; - gcry_mpi_release (params->prime); - gcry_mpi_release (params->base); - g_free (params); -} - -void -egg_dh_pubkey_free (egg_dh_pubkey *pubkey) -{ - if (!pubkey) - return; - if (pubkey->inner) - gcry_mpi_release (pubkey->inner); - g_free (pubkey); -} - -void -egg_dh_privkey_free (egg_dh_privkey *privkey) -{ - if (!privkey) - return; - if (privkey->inner) - gcry_mpi_release (privkey->inner); - g_free (privkey); -} - -GBytes * -egg_dh_pubkey_export (const egg_dh_pubkey *pubkey) -{ - gcry_error_t gcry; - unsigned char *buffer; - size_t n_buffer; - - gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, - pubkey->inner); - g_return_val_if_fail (gcry == 0, NULL); - - return g_bytes_new_with_free_func (buffer, n_buffer, - gcry_free, buffer); -} - -egg_dh_pubkey * -egg_dh_pubkey_new_from_bytes (const egg_dh_params *params, - GBytes *bytes) -{ - gcry_error_t gcry; - gcry_mpi_t inner; - egg_dh_pubkey *pub; - - gcry = gcry_mpi_scan (&inner, GCRYMPI_FMT_USG, - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), - NULL); - g_return_val_if_fail (gcry == 0, NULL); - - pub = g_new (struct egg_dh_pubkey, 1); - if (!pub) { - gcry_mpi_release (inner); - g_return_val_if_reached (NULL); - } - - pub->inner = inner; - return pub; -} diff --git a/egg/egg-dh.h b/egg/egg-dh.h index 0883ec1..3ae856f 100644 --- a/egg/egg-dh.h +++ b/egg/egg-dh.h @@ -30,6 +30,17 @@ typedef struct egg_dh_params egg_dh_params; typedef struct egg_dh_pubkey egg_dh_pubkey; typedef struct egg_dh_privkey egg_dh_privkey; +typedef struct egg_dh_group { + const gchar *name; + guint bits; + const guchar *prime; + gsize n_prime; + const guchar base[1]; + gsize n_base; +} egg_dh_group; + +extern const egg_dh_group egg_dh_groups[]; + egg_dh_params *egg_dh_default_params (const gchar *name); gboolean egg_dh_default_params_raw (const gchar *name, diff --git a/egg/egg-hkdf-gnutls.c b/egg/egg-hkdf-gnutls.c new file mode 100644 index 0000000..2b95d39 --- /dev/null +++ b/egg/egg-hkdf-gnutls.c @@ -0,0 +1,110 @@ +/* + * libsecret + * + * Copyright (C) 2023 Daiki Ueno + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received copies of the GNU General Public License and + * the GNU Lesser General Public License along with this program. If + * not, see http://www.gnu.org/licenses/. + * + * Author: Daiki Ueno + */ + +#include "config.h" + +#include "egg-hkdf.h" + +#include "egg-secure-memory.h" +#include + +EGG_SECURE_DECLARE (hkdf); + +gboolean +egg_hkdf_perform (const gchar *hash_algo, gconstpointer input, gsize n_input, + gconstpointer salt, gsize n_salt, gconstpointer info, + gsize n_info, gpointer output, gsize n_output) +{ + static const struct { + gchar *name; + gnutls_mac_algorithm_t algo; + } hash_algos[] = { + { "sha1", GNUTLS_MAC_SHA1 }, + { "sha256", GNUTLS_MAC_SHA256 }, + { NULL, } + }; + gnutls_mac_algorithm_t algo = GNUTLS_MAC_UNKNOWN; + gnutls_datum_t input_datum, salt_datum, info_datum; + gpointer alloc = NULL; + gpointer buffer = NULL; + guint hash_len; + gsize i; + int ret; + gboolean result = TRUE; + + for (i = 0; hash_algos[i].name; i++) { + if (g_str_equal (hash_algos[i].name, hash_algo)) { + algo = hash_algos[i].algo; + break; + } + } + g_return_val_if_fail (algo != GNUTLS_MAC_UNKNOWN, FALSE); + + hash_len = gnutls_hmac_get_len (algo); + g_return_val_if_fail (hash_len != 0, FALSE); + g_return_val_if_fail (n_output <= 255 * hash_len, FALSE); + + /* Buffer we need to for intermediate stuff */ + buffer = egg_secure_alloc (hash_len); + g_return_val_if_fail (buffer, FALSE); + + /* Salt defaults to hash_len zeros */ + if (!salt) { + alloc = g_malloc0 (hash_len); + if (!alloc) { + result = FALSE; + goto out; + } + salt = alloc; + n_salt = hash_len; + } + + /* Step 1: Extract */ + input_datum.data = (void *)input; + input_datum.size = n_input; + salt_datum.data = (void *)salt; + salt_datum.size = n_salt; + + ret = gnutls_hkdf_extract (algo, &input_datum, &salt_datum, buffer); + if (ret < 0) { + result = FALSE; + goto out; + } + + /* Step 2: Expand */ + input_datum.data = buffer; + input_datum.size = hash_len; + info_datum.data = (void *)info; + info_datum.size = n_info; + + ret = gnutls_hkdf_expand (algo, &input_datum, &info_datum, + output, n_output); + if (ret < 0) { + result = FALSE; + goto out; + } + + out: + g_free (alloc); + egg_secure_free (buffer); + return result; +} diff --git a/egg/egg-hkdf.c b/egg/egg-hkdf-libgcrypt.c similarity index 91% rename from egg/egg-hkdf.c rename to egg/egg-hkdf-libgcrypt.c index bc2919a..78fe8d4 100644 --- a/egg/egg-hkdf.c +++ b/egg/egg-hkdf-libgcrypt.c @@ -13,10 +13,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA + * You should have received copies of the GNU General Public License and + * the GNU Lesser General Public License along with this program. If + * not, see http://www.gnu.org/licenses/. * * Author: Stef Walter */ @@ -24,10 +23,8 @@ #include "config.h" #include "egg-hkdf.h" -#include "egg-secure-memory.h" #include - #include gboolean diff --git a/egg/egg-keyring1-gnutls.c b/egg/egg-keyring1-gnutls.c new file mode 100644 index 0000000..ad24d9d --- /dev/null +++ b/egg/egg-keyring1-gnutls.c @@ -0,0 +1,156 @@ +/* libsecret - GLib wrapper for Secret Service + * + * Copyright 2019 Red Hat, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Daiki Ueno + */ + +#include "config.h" + +#include "egg-keyring1.h" + +#include "egg/egg-secure-memory.h" + +EGG_SECURE_DECLARE (egg_keyring1); + +#include +#define PBKDF2_HASH_ALGO GNUTLS_MAC_SHA256 +#define MAC_ALGO GNUTLS_MAC_SHA256 +#define CIPHER_ALGO GNUTLS_CIPHER_AES_128_CBC + +void +egg_keyring1_create_nonce (guint8 *nonce, + gsize nonce_size) +{ + (void)gnutls_rnd (GNUTLS_RND_NONCE, nonce, nonce_size); +} + +GBytes * +egg_keyring1_derive_key (const char *password, + gsize n_password, + GBytes *salt, + guint32 iteration_count) +{ + gnutls_datum_t password_datum, salt_datum; + guint8 *buffer; + int ret; + + password_datum.data = (void *)password; + password_datum.size = n_password; + + salt_datum.data = (void *)g_bytes_get_data (salt, NULL); + salt_datum.size = g_bytes_get_size (salt); + + buffer = egg_secure_alloc (KEY_SIZE); + g_return_val_if_fail (buffer, NULL); + + ret = gnutls_pbkdf2 (PBKDF2_HASH_ALGO, &password_datum, &salt_datum, + iteration_count, + buffer, KEY_SIZE); + if (ret < 0) { + egg_secure_free (buffer); + return NULL; + } + + return g_bytes_new_with_free_func (buffer, + KEY_SIZE, + egg_secure_free, + buffer); +} + +gboolean +egg_keyring1_calculate_mac (GBytes *key, + const guint8 *value, + gsize n_value, + guint8 *buffer) +{ + return gnutls_hmac_fast (MAC_ALGO, + g_bytes_get_data (key, NULL), + g_bytes_get_size (key), + value, n_value, + buffer) >= 0; +} + +gboolean +egg_keyring1_verify_mac (GBytes *key, + const guint8 *value, + gsize n_value, + const guint8 *data) +{ + guint8 buffer[MAC_SIZE]; + guint8 status = 0; + gsize i; + + if (!egg_keyring1_calculate_mac (key, value, n_value, buffer)) { + return FALSE; + } + + for (i = 0; i < MAC_SIZE; i++) { + status |= data[i] ^ buffer[i]; + } + + return status == 0; +} + +gboolean +egg_keyring1_decrypt (GBytes *key, + guint8 *data, + gsize n_data) +{ + gnutls_cipher_hd_t hd = NULL; + int ret; + gsize n_secret; + gnutls_datum_t key_datum, iv_datum; + + key_datum.data = (void *)g_bytes_get_data (key, &n_secret); + key_datum.size = n_secret; + + iv_datum.data = data + n_data; + iv_datum.size = IV_SIZE; + + ret = gnutls_cipher_init (&hd, CIPHER_ALGO, &key_datum, &iv_datum); + if (ret < 0) { + return FALSE; + } + + ret = gnutls_cipher_decrypt2 (hd, data, n_data, data, n_data); + if (ret < 0) { + gnutls_cipher_deinit (hd); + return FALSE; + } + + gnutls_cipher_deinit (hd); + return TRUE; +} + +gboolean +egg_keyring1_encrypt (GBytes *key, + guint8 *data, + gsize n_data) +{ + gnutls_cipher_hd_t hd = NULL; + int ret; + gsize n_secret; + gnutls_datum_t key_datum, iv_datum; + + key_datum.data = (void *)g_bytes_get_data (key, &n_secret); + key_datum.size = n_secret; + + iv_datum.data = data + n_data; + iv_datum.size = IV_SIZE; + egg_keyring1_create_nonce (iv_datum.data, iv_datum.size); + + ret = gnutls_cipher_init (&hd, CIPHER_ALGO, &key_datum, &iv_datum); + g_return_val_if_fail (ret >= 0, FALSE); + + ret = gnutls_cipher_encrypt2 (hd, data, n_data, data, n_data); + gnutls_cipher_deinit (hd); + return ret < 0 ? FALSE : TRUE; +} diff --git a/egg/egg-keyring1.c b/egg/egg-keyring1-libgcrypt.c similarity index 100% rename from egg/egg-keyring1.c rename to egg/egg-keyring1-libgcrypt.c diff --git a/egg/meson.build b/egg/meson.build index 04a305c..cf88390 100644 --- a/egg/meson.build +++ b/egg/meson.build @@ -8,15 +8,27 @@ libegg_deps = [ glib_deps, ] -if get_option('gcrypt') +if with_crypto libegg_sources += [ 'egg-dh.c', - 'egg-hkdf.c', - 'egg-keyring1.c', - 'egg-libgcrypt.c', ] - libegg_deps += gcrypt_dep + if with_gcrypt + libegg_sources += [ + 'egg-dh-libgcrypt.c', + 'egg-hkdf-libgcrypt.c', + 'egg-keyring1-libgcrypt.c', + 'egg-libgcrypt.c', + ] + elif with_gnutls + libegg_sources += [ + 'egg-dh-gnutls.c', + 'egg-hkdf-gnutls.c', + 'egg-keyring1-gnutls.c', + ] + endif + + libegg_deps += crypto_deps endif if get_option('tpm2') @@ -39,7 +51,7 @@ test_names = [ 'test-secmem', ] -if get_option('gcrypt') +if with_crypto test_names += [ 'test-dh', 'test-hkdf', diff --git a/egg/test-dh.c b/egg/test-dh.c index 05831c0..3b171cf 100644 --- a/egg/test-dh.c +++ b/egg/test-dh.c @@ -33,7 +33,6 @@ #include #include -#include EGG_SECURE_DEFINE_GLIB_GLOBALS (); @@ -107,10 +106,8 @@ static void check_dh_default (const gchar *name, guint bits) { gboolean ret; - gcry_mpi_t check; gconstpointer prime, base; gsize n_prime, n_base; - gcry_error_t gcry; ret = egg_dh_default_params_raw (name, &prime, &n_prime, &base, &n_base); g_assert_true (ret); @@ -118,14 +115,6 @@ check_dh_default (const gchar *name, guint bits) egg_assert_cmpsize (n_prime, >, 0); g_assert_nonnull (base); egg_assert_cmpsize (n_base, >, 0); - - gcry = gcry_mpi_scan (&check, GCRYMPI_FMT_USG, prime, n_prime, NULL); - g_assert_true (gcry == 0); - gcry_mpi_release (check); - - gcry = gcry_mpi_scan (&check, GCRYMPI_FMT_USG, base, n_base, NULL); - g_assert_true (gcry == 0); - gcry_mpi_release (check); } static void diff --git a/libsecret/meson.build b/libsecret/meson.build index 97e0222..85ad68f 100644 --- a/libsecret/meson.build +++ b/libsecret/meson.build @@ -36,7 +36,7 @@ libsecret_headers = [ 'secret-value.h', ] -if get_option('gcrypt') +if with_crypto libsecret_sources += [ 'secret-file-backend.c', 'secret-file-collection.c', @@ -76,8 +76,8 @@ libsecret_dependencies = [ glib_deps, ] -if get_option('gcrypt') - libsecret_dependencies += gcrypt_dep +if with_crypto + libsecret_dependencies += crypto_deps endif libsecret_cflags = [ @@ -231,7 +231,7 @@ test_names = [ 'test-collection', ] -if get_option('gcrypt') +if with_crypto test_names += [ 'test-file-collection', ] diff --git a/libsecret/secret-backend.c b/libsecret/secret-backend.c index 9b3c22b..75c42da 100644 --- a/libsecret/secret-backend.c +++ b/libsecret/secret-backend.c @@ -16,7 +16,7 @@ #include "secret-backend.h" -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO #include "secret-file-backend.h" #endif @@ -148,11 +148,11 @@ backend_get_impl_type (void) GIOExtensionPoint *ep; g_type_ensure (secret_service_get_type ()); -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO g_type_ensure (secret_file_backend_get_type ()); #endif -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO if ((g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS) || g_getenv ("SNAP_NAME") != NULL) && _secret_file_backend_check_portal_version ()) extension_name = "file"; diff --git a/libsecret/secret-file-collection.c b/libsecret/secret-file-collection.c index bc2b600..b33caad 100644 --- a/libsecret/secret-file-collection.c +++ b/libsecret/secret-file-collection.c @@ -17,11 +17,14 @@ #include "secret-file-collection.h" #include "egg/egg-keyring1.h" -#include "egg/egg-libgcrypt.h" #include "egg/egg-secure-memory.h" EGG_SECURE_DECLARE (secret_file_collection); +#ifdef WITH_GCRYPT +#include "egg/egg-libgcrypt.h" +#endif + #define KEYRING_FILE_HEADER "GnomeKeyring\n\r\0\n" #define KEYRING_FILE_HEADER_LEN 16 @@ -143,8 +146,9 @@ secret_file_collection_class_init (SecretFileCollectionClass *klass) g_param_spec_boxed ("password", "password", "Password", SECRET_TYPE_VALUE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - +#ifdef WITH_GCRYPT egg_libgcrypt_initialize (); +#endif } static gboolean diff --git a/libsecret/secret-session.c b/libsecret/secret-session.c index 0fecbff..375984c 100644 --- a/libsecret/secret-session.c +++ b/libsecret/secret-session.c @@ -17,13 +17,20 @@ #include "secret-private.h" -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO #include "egg/egg-dh.h" #include "egg/egg-hkdf.h" +#endif + +#ifdef WITH_GCRYPT #include "egg/egg-libgcrypt.h" #include #endif +#ifdef WITH_GNUTLS +#include +#endif + #include "egg/egg-hex.h" #include "egg/egg-secure-memory.h" @@ -37,7 +44,7 @@ EGG_SECURE_DECLARE (secret_session); struct _SecretSession { gchar *path; const gchar *algorithms; -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO egg_dh_params *params; egg_dh_privkey *privat; egg_dh_pubkey *publi; @@ -55,7 +62,7 @@ _secret_session_free (gpointer data) return; g_free (session->path); -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO egg_dh_pubkey_free (session->publi); egg_dh_privkey_free (session->privat); egg_dh_params_free (session->params); @@ -64,7 +71,7 @@ _secret_session_free (gpointer data) g_free (session); } -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO static GVariant * request_open_session_aes (SecretSession *session) @@ -76,7 +83,9 @@ request_open_session_aes (SecretSession *session) g_assert (session->privat == NULL); g_assert (session->publi == NULL); +#ifdef WITH_GCRYPT egg_libgcrypt_initialize (); +#endif /* Initialize our local parameters and values */ session->params = egg_dh_default_params ("ietf-ike-grp-modp-1024"); @@ -168,7 +177,7 @@ response_open_session_aes (SecretSession *session, return TRUE; } -#endif /* WITH_GCRYPT */ +#endif /* WITH_CRYPTO */ static GVariant * request_open_session_plain (SecretSession *session) @@ -251,7 +260,7 @@ on_service_open_session_plain (GObject *source, g_object_unref (task); } -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO static void on_service_open_session_aes (GObject *source, @@ -300,7 +309,7 @@ on_service_open_session_aes (GObject *source, g_object_unref (task); } -#endif /* WITH_GCRYPT */ +#endif /* WITH_CRYPTO */ void @@ -319,7 +328,7 @@ _secret_session_open (SecretService *service, g_task_set_task_data (task, closure, open_session_closure_free); g_dbus_proxy_call (G_DBUS_PROXY (service), "OpenSession", -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO request_open_session_aes (closure->session), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, on_service_open_session_aes, @@ -345,7 +354,7 @@ _secret_session_open_finish (GAsyncResult *result, return TRUE; } -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO static gboolean pkcs7_unpad_bytes_in_place (guchar *padded, @@ -377,6 +386,8 @@ pkcs7_unpad_bytes_in_place (guchar *padded, return TRUE; } +#ifdef WITH_GCRYPT + static SecretValue * service_decode_aes_secret (SecretSession *session, gconstpointer param, @@ -447,6 +458,85 @@ service_decode_aes_secret (SecretSession *session, #endif /* WITH_GCRYPT */ +#ifdef WITH_GNUTLS + +static SecretValue * +service_decode_aes_secret (SecretSession *session, + gconstpointer param, + gsize n_param, + gconstpointer value, + gsize n_value, + const gchar *content_type) +{ + gnutls_cipher_hd_t cih; + gnutls_datum_t iv, key; + gsize n_padded; + int ret; + guchar *padded; + + if (n_param != 16) { + g_info ("received an encrypted secret structure with invalid parameter"); + return NULL; + } + + if (n_value == 0 || n_value % 16 != 0) { + g_info ("received an encrypted secret structure with bad secret length"); + return NULL; + } + +#if 0 + g_printerr (" lib iv: %s\n", egg_hex_encode (param, n_param)); +#endif + +#if 0 + g_printerr (" lib key: %s\n", egg_hex_encode (session->key, session->n_key)); +#endif + + iv.data = (void *)param; + iv.size = n_param; + key.data = session->key; + key.size = session->n_key; + ret = gnutls_cipher_init (&cih, GNUTLS_CIPHER_AES_128_CBC, &key, &iv); + if (ret != 0) { + g_warning ("couldn't create AES cipher: %s", gnutls_strerror (ret)); + return NULL; + } + + /* Copy the memory buffer */ + n_padded = n_value; + padded = egg_secure_alloc (n_padded); + if (!padded) { + gnutls_cipher_deinit (cih); + return NULL; + } + memcpy (padded, value, n_padded); + + /* Perform the decryption */ + ret = gnutls_cipher_decrypt2 (cih, padded, n_padded, padded, n_padded); + if (ret < 0) { + egg_secure_clear (padded, n_padded); + egg_secure_free (padded); + gnutls_cipher_deinit (cih); + return NULL; + } + + gnutls_cipher_deinit (cih); + + /* Unpad the resulting value */ + if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) { + egg_secure_clear (padded, n_padded); + egg_secure_free (padded); + g_info ("received an invalid or unencryptable secret"); + return FALSE; + } + + return secret_value_new_full ((gchar *)padded, n_padded, content_type, egg_secure_free); +} + +#endif /* WITH_GNUTLS */ + +#endif /* WITH_CRYPTO */ + static SecretValue * service_decode_plain_secret (SecretSession *session, gconstpointer param, @@ -496,7 +586,7 @@ _secret_session_decode_secret (SecretSession *session, value = g_variant_get_fixed_array (vvalue, &n_value, sizeof (guchar)); g_variant_get_child (encoded, 3, "s", &content_type); -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO if (session->key != NULL) result = service_decode_aes_secret (session, param, n_param, value, n_value, content_type); @@ -513,7 +603,7 @@ _secret_session_decode_secret (SecretSession *session, return result; } -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO static guchar* pkcs7_pad_bytes_in_secure_memory (gconstpointer secret, @@ -535,6 +625,8 @@ pkcs7_pad_bytes_in_secure_memory (gconstpointer secret, return padded; } +#ifdef WITH_GCRYPT + static gboolean service_encode_aes_secret (SecretSession *session, SecretValue *value, @@ -594,6 +686,72 @@ service_encode_aes_secret (SecretSession *session, #endif /* WITH_GCRYPT */ +#ifdef WITH_GNUTLS + +static gboolean +service_encode_aes_secret (SecretSession *session, + SecretValue *value, + GVariantBuilder *builder) +{ + gnutls_cipher_hd_t cih = NULL; + guchar *padded; + gsize n_padded; + int ret; + gnutls_datum_t iv, key; + gpointer iv_data; + gconstpointer secret; + gsize n_secret; + GVariant *child; + + g_variant_builder_add (builder, "o", session->path); + + secret = secret_value_get (value, &n_secret); + + /* Perform the encoding here */ + padded = pkcs7_pad_bytes_in_secure_memory (secret, n_secret, &n_padded); + g_assert (padded != NULL); + + /* Setup the IV */ + iv_data = g_malloc0 (16); + iv.data = iv_data; + iv.size = 16; + ret = gnutls_rnd (GNUTLS_RND_NONCE, iv.data, iv.size); + g_return_val_if_fail (ret >= 0, FALSE); + + /* Setup the key */ + key.data = session->key; + key.size = session->n_key; + + /* Create the cipher */ + ret = gnutls_cipher_init (&cih, GNUTLS_CIPHER_AES_128_CBC, &key, &iv); + if (ret < 0) { + g_warning ("couldn't create AES cipher: %s", gnutls_strerror (ret)); + return FALSE; + } + + /* Perform the encryption */ + ret = gnutls_cipher_encrypt2 (cih, padded, n_padded, padded, n_padded); + if (ret < 0) { + gnutls_cipher_deinit (cih); + return FALSE; + } + + gnutls_cipher_deinit (cih); + + child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), iv_data, 16, TRUE, g_free, iv_data); + g_variant_builder_add_value (builder, child); + + child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), padded, n_padded, TRUE, egg_secure_free, padded); + g_variant_builder_add_value (builder, child); + + g_variant_builder_add (builder, "s", secret_value_get_content_type (value)); + return TRUE; +} + +#endif /* WITH_GNUTLS */ + +#endif /* WITH_CRYPTO */ + static gboolean service_encode_plain_secret (SecretSession *session, SecretValue *value, @@ -633,7 +791,7 @@ _secret_session_encode_secret (SecretSession *session, type = g_variant_type_new ("(oayays)"); builder = g_variant_builder_new (type); -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO if (session->key) ret = service_encode_aes_secret (session, value, builder); else diff --git a/libsecret/test-session.c b/libsecret/test-session.c index c94f2fa..d0ad881 100644 --- a/libsecret/test-session.c +++ b/libsecret/test-session.c @@ -29,7 +29,7 @@ #include #include -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO #define SESSION_ALGO "dh-ietf1024-sha256-aes128-cbc-pkcs7" #else #define SESSION_ALGO "plain" diff --git a/meson.build b/meson.build index e83321d..65fee25 100644 --- a/meson.build +++ b/meson.build @@ -30,11 +30,37 @@ glib_deps = [ dependency('gio-unix-2.0', version: '>=' + min_glib_version), ] -min_libgcrypt_version = '1.2.2' -gcrypt_dep = dependency('libgcrypt', - version: '>=' + min_libgcrypt_version, - required: get_option('gcrypt'), -) +with_gcrypt = false +with_gnutls = false +with_crypto = false + +crypto_deps = [] + +if get_option('crypto') == 'libgcrypt' + min_libgcrypt_version = '1.2.2' + gcrypt_dep = dependency( + 'libgcrypt', + version: '>=' + min_libgcrypt_version, + required: false, + ) + if gcrypt_dep.found() + with_gcrypt = true + with_crypto = true + crypto_deps += gcrypt_dep + endif +elif get_option('crypto') == 'gnutls' + min_gnutls_version = '3.8.2' + gnutls_dep = dependency( + 'gnutls', + version: '>=' + min_gnutls_version, + required: false, + ) + if gnutls_dep.found() + with_gnutls = true + with_crypto = true + crypto_deps += gnutls_dep + endif +endif min_tss2_version = '3.0.3' tss2_esys = dependency('tss2-esys', version: '>=' + min_tss2_version, required: get_option('tpm2')) @@ -58,9 +84,11 @@ conf.set_quoted('LOCALEDIR', libsecret_prefix / get_option('localedir')) conf.set_quoted('PACKAGE_NAME', meson.project_name()) conf.set_quoted('PACKAGE_STRING', meson.project_name()) conf.set_quoted('PACKAGE_VERSION', meson.project_version()) -conf.set('WITH_GCRYPT', get_option('gcrypt')) +conf.set('WITH_GCRYPT', with_gcrypt) +conf.set('WITH_GNUTLS', with_gnutls) +conf.set('WITH_CRYPTO', with_crypto) conf.set('WITH_TPM', get_option('tpm2')) -if get_option('gcrypt') +if with_gcrypt conf.set_quoted('LIBGCRYPT_VERSION', min_libgcrypt_version) endif if get_option('tpm2') diff --git a/meson_options.txt b/meson_options.txt index d6a6060..7c6f5ee 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,5 +1,5 @@ option('manpage', type: 'boolean', value: true, description: 'Build man pages') -option('gcrypt', type: 'boolean', value: true, description: 'With gcrypt and transport encryption') +option('crypto', type: 'combo', choices: ['libgcrypt', 'gnutls', 'disabled'], value: 'libgcrypt', description: 'Backend library to implement transport encryption') option('debugging', type: 'boolean', value: false, description: 'Turn debugging on/off') option('vapi', type: 'boolean', value: true, description: 'Create VAPI file.') option('gtk_doc', type: 'boolean', value: true, description: 'Build reference documentation using gi-docgen') diff --git a/tool/meson.build b/tool/meson.build index 642cd59..a1b3c2b 100644 --- a/tool/meson.build +++ b/tool/meson.build @@ -10,7 +10,7 @@ secret_tool = executable('secret-tool', install: true, ) -if get_option('gcrypt') and host_machine.system() != 'windows' +if with_crypto and host_machine.system() != 'windows' test('test-secret-tool.sh', find_program('test-secret-tool.sh'), env: test_env,