diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index da00840..10cc3e3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: registry.gitlab.gnome.org/gnome/libsecret/master:v2 +image: registry.gitlab.gnome.org/gnome/libsecret/master:v3 stages: - build @@ -7,12 +7,21 @@ stages: variables: CPPCHECK_OPTIONS: "--enable=warning --enable=style --enable=performance --enable=portability --std=c99 --template='{id}:{file}:{line},{severity},{message}'" +.build: + parallel: + matrix: + - CRYPTO: libgcrypt + - CRYPTO: gnutls + - CRYPTO: disabled + fedora:Werror: stage: build + extends: + - .build before_script: - dbus-uuidgen --ensure script: - - meson _build -Dwerror=true -Dc_args=-Wno-error=deprecated-declarations -Dgtk_doc=false + - meson _build -Dwerror=true -Dc_args=-Wno-error=deprecated-declarations -Dgtk_doc=false -Dcrypto=$CRYPTO - meson compile -C _build - eval `dbus-launch --sh-syntax` - meson test -C _build --print-errorlogs @@ -23,11 +32,13 @@ fedora:Werror: fedora:asan: stage: build + extends: + - .build before_script: - dbus-uuidgen --ensure script: - export LSAN_OPTIONS=suppressions=$PWD/build/lsan.supp - - meson _build -Db_sanitize=address -Dgtk_doc=false -Dintrospection=false + - meson _build -Db_sanitize=address -Dgtk_doc=false -Dintrospection=false -Dcrypto=$CRYPTO - meson compile -C _build - eval `dbus-launch --sh-syntax` - meson test -C _build --print-errorlogs @@ -38,10 +49,12 @@ fedora:asan: fedora:ubsan: stage: build + extends: + - .build before_script: - dbus-uuidgen --ensure script: - - meson _build -Db_sanitize=undefined -Dgtk_doc=false + - meson _build -Db_sanitize=undefined -Dgtk_doc=false -Dcrypto=$CRYPTO - meson compile -C _build - eval `dbus-launch --sh-syntax` - meson test -C _build --print-errorlogs @@ -52,10 +65,12 @@ fedora:ubsan: fedora-static-analyzers/test: stage: build + extends: + - .build before_script: - dbus-uuidgen --ensure script: - - meson _build -Dgtk_doc=false + - meson _build -Dgtk_doc=false -Dcrypto=$CRYPTO - meson compile -C _build --ninja-args scan-build - cppcheck --force -q $CPPCHECK_OPTIONS libsecret/ egg/ tool/ artifacts: diff --git a/.gitlab-ci/master.Dockerfile b/.gitlab-ci/master.Dockerfile index 56c8672..b353a6e 100644 --- a/.gitlab-ci/master.Dockerfile +++ b/.gitlab-ci/master.Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:38 +FROM fedora:39 RUN dnf update -y \ && dnf install -y \ @@ -10,6 +10,7 @@ RUN dnf update -y \ gi-docgen \ git \ glib2-devel \ + gnutls-devel \ gobject-introspection-devel \ lcov \ libasan \ 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 b190488..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,21 +23,6 @@ #include "config.h" #include "egg-dh.h" -#include "egg-secure-memory.h" - -/* 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, @@ -171,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), @@ -216,7 +200,7 @@ 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); @@ -224,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; @@ -236,127 +220,3 @@ egg_dh_default_params_raw (const gchar *name, gconstpointer *prime, return FALSE; } - -gboolean -egg_dh_default_params (const gchar *name, gcry_mpi_t *prime, gcry_mpi_t *base) -{ - const DHGroup *group; - gcry_error_t gcry; - - g_return_val_if_fail (name, FALSE); - - for (group = dh_groups; group->name; ++group) { - if (g_str_equal (group->name, name)) { - if (prime) { - gcry = gcry_mpi_scan (prime, GCRYMPI_FMT_USG, group->prime, group->n_prime, NULL); - g_return_val_if_fail (gcry == 0, FALSE); - g_return_val_if_fail (gcry_mpi_get_nbits (*prime) == group->bits, FALSE); - } - if (base) { - gcry = gcry_mpi_scan (base, GCRYMPI_FMT_USG, group->base, group->n_base, NULL); - g_return_val_if_fail (gcry == 0, FALSE); - } - return TRUE; - } - } - - return FALSE; -} - -gboolean -egg_dh_gen_pair (gcry_mpi_t prime, gcry_mpi_t base, guint bits, - gcry_mpi_t *pub, gcry_mpi_t *priv) -{ - guint pbits; - - g_return_val_if_fail (prime, FALSE); - g_return_val_if_fail (base, FALSE); - g_return_val_if_fail (pub, FALSE); - g_return_val_if_fail (priv, FALSE); - - pbits = gcry_mpi_get_nbits (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 = gcry_mpi_snew (bits); - g_return_val_if_fail (*priv, FALSE); - while (gcry_mpi_cmp_ui (*priv, 0) == 0) - gcry_mpi_randomize (*priv, bits, GCRY_STRONG_RANDOM); - - /* Secret key value must be less than half of p */ - if (gcry_mpi_get_nbits (*priv) > bits) - gcry_mpi_clear_highbit (*priv, bits); - if (gcry_mpi_get_nbits (*priv) > pbits - 1) - gcry_mpi_clear_highbit (*priv, pbits - 1); - g_assert (gcry_mpi_cmp (prime, *priv) > 0); - - *pub = gcry_mpi_new (gcry_mpi_get_nbits (*priv)); - g_return_val_if_fail (*pub, FALSE); - gcry_mpi_powm (*pub, base, *priv, prime); - - return TRUE; -} - -gpointer -egg_dh_gen_secret (gcry_mpi_t peer, gcry_mpi_t priv, - gcry_mpi_t prime, gsize *bytes) -{ - 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 (prime, NULL); - - bits = gcry_mpi_get_nbits (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, priv, prime); - - /* Write out the secret */ - gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n_prime, 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); - - *bytes = n_prime; - -#if DEBUG_DH_SECRET - gcry_mpi_scan (&k, GCRYMPI_FMT_USG, value, bytes, NULL); - g_printerr ("RAW SECRET: "); - gcry_mpi_dump (k); - gcry_mpi_release (k); -#endif - - return value; -} diff --git a/egg/egg-dh.h b/egg/egg-dh.h index 679aa19..3ae856f 100644 --- a/egg/egg-dh.h +++ b/egg/egg-dh.h @@ -26,27 +26,44 @@ #include -#include +typedef struct egg_dh_params egg_dh_params; +typedef struct egg_dh_pubkey egg_dh_pubkey; +typedef struct egg_dh_privkey egg_dh_privkey; -gboolean egg_dh_default_params (const gchar *name, - gcry_mpi_t *prime, - gcry_mpi_t *base); +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; -gboolean egg_dh_default_params_raw (const gchar *name, - gconstpointer *prime, - gsize *n_prime, - gconstpointer *base, - gsize *n_base); +extern const egg_dh_group egg_dh_groups[]; -gboolean egg_dh_gen_pair (gcry_mpi_t prime, - gcry_mpi_t base, - guint bits, - gcry_mpi_t *pub, - gcry_mpi_t *priv); +egg_dh_params *egg_dh_default_params (const gchar *name); -gpointer egg_dh_gen_secret (gcry_mpi_t peer, - gcry_mpi_t priv, - gcry_mpi_t prime, - gsize *bytes); +gboolean egg_dh_default_params_raw (const gchar *name, + gconstpointer *prime, + gsize *n_prime, + gconstpointer *base, + gsize *n_base); + +gboolean egg_dh_gen_pair (egg_dh_params *params, + guint bits, + egg_dh_pubkey **pub, + egg_dh_privkey **priv); + +GBytes *egg_dh_gen_secret (egg_dh_pubkey *peer, + egg_dh_privkey *priv, + egg_dh_params *prime); + +void egg_dh_params_free (egg_dh_params *params); +void egg_dh_pubkey_free (egg_dh_pubkey *pubkey); +void egg_dh_privkey_free (egg_dh_privkey *privkey); + +GBytes *egg_dh_pubkey_export (const egg_dh_pubkey *pubkey); +egg_dh_pubkey *egg_dh_pubkey_new_from_bytes (const egg_dh_params *params, + GBytes *bytes); #endif /* EGG_DH_H_ */ 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-libgcrypt.c b/egg/egg-keyring1-libgcrypt.c new file mode 100644 index 0000000..bb987be --- /dev/null +++ b/egg/egg-keyring1-libgcrypt.c @@ -0,0 +1,193 @@ +/* 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 GCRY_MD_SHA256 +#define MAC_ALGO GCRY_MAC_HMAC_SHA256 +#define CIPHER_ALGO GCRY_CIPHER_AES128 + +void +egg_keyring1_create_nonce (guint8 *nonce, + gsize nonce_size) +{ + gcry_create_nonce (nonce, nonce_size); +} + +GBytes * +egg_keyring1_derive_key (const gchar *password, + gsize n_password, + GBytes *salt, + guint32 iteration_count) +{ + guint8 *buffer; + gcry_error_t gcry; + + buffer = egg_secure_alloc (KEY_SIZE); + g_return_val_if_fail (buffer, NULL); + + gcry = gcry_kdf_derive (password, + n_password, + GCRY_KDF_PBKDF2, PBKDF2_HASH_ALGO, + g_bytes_get_data (salt, NULL), + g_bytes_get_size (salt), + iteration_count, + KEY_SIZE, buffer); + if (gcry != 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) +{ + gcry_mac_hd_t hd; + gcry_error_t gcry; + gconstpointer secret; + gsize n_secret; + gboolean ret = FALSE; + + gcry = gcry_mac_open (&hd, MAC_ALGO, 0, NULL); + g_return_val_if_fail (gcry == 0, FALSE); + + secret = g_bytes_get_data (key, &n_secret); + gcry = gcry_mac_setkey (hd, secret, n_secret); + if (gcry != 0) + goto out; + + gcry = gcry_mac_write (hd, value, n_value); + if (gcry != 0) + goto out; + + n_value = MAC_SIZE; + gcry = gcry_mac_read (hd, buffer, &n_value); + if (gcry != 0) + goto out; + + if (n_value != MAC_SIZE) + goto out; + + ret = TRUE; + out: + gcry_mac_close (hd); + return ret; +} + +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) +{ + gcry_cipher_hd_t hd; + gcry_error_t gcry; + gconstpointer secret; + gsize n_secret; + gboolean ret = FALSE; + + gcry = gcry_cipher_open (&hd, CIPHER_ALGO, GCRY_CIPHER_MODE_CBC, 0); + if (gcry != 0) + goto out; + + secret = g_bytes_get_data (key, &n_secret); + gcry = gcry_cipher_setkey (hd, secret, n_secret); + if (gcry != 0) + goto out; + + gcry = gcry_cipher_setiv (hd, data + n_data, IV_SIZE); + if (gcry != 0) + goto out; + + gcry = gcry_cipher_decrypt (hd, data, n_data, NULL, 0); + if (gcry != 0) + goto out; + + ret = TRUE; + out: + gcry_cipher_close (hd); + return ret; +} + +gboolean +egg_keyring1_encrypt (GBytes *key, + guint8 *data, + gsize n_data) +{ + gcry_cipher_hd_t hd; + gcry_error_t gcry; + gconstpointer secret; + gsize n_secret; + gboolean ret = FALSE; + + gcry = gcry_cipher_open (&hd, CIPHER_ALGO, GCRY_CIPHER_MODE_CBC, 0); + if (gcry != 0) + goto out; + + secret = g_bytes_get_data (key, &n_secret); + gcry = gcry_cipher_setkey (hd, secret, n_secret); + if (gcry != 0) + goto out; + + egg_keyring1_create_nonce (data + n_data, IV_SIZE); + + gcry = gcry_cipher_setiv (hd, data + n_data, IV_SIZE); + if (gcry != 0) + goto out; + + gcry = gcry_cipher_encrypt (hd, data, n_data, NULL, 0); + if (gcry != 0) + goto out; + + ret = TRUE; + out: + gcry_cipher_close (hd); + return ret; +} diff --git a/egg/egg-keyring1.h b/egg/egg-keyring1.h new file mode 100644 index 0000000..e733420 --- /dev/null +++ b/egg/egg-keyring1.h @@ -0,0 +1,55 @@ +/* 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 + */ + +#ifndef EGG_KEYRING1_H_ +#define EGG_KEYRING1_H_ + +#include + +#define SALT_SIZE 32 +#define ITERATION_COUNT 100000 + +#define MAC_SIZE 32 + +#define CIPHER_BLOCK_SIZE 16 +#define KEY_SIZE 16 +#define IV_SIZE CIPHER_BLOCK_SIZE + +void egg_keyring1_create_nonce (guint8 *nonce, + gsize nonce_size); + +GBytes *egg_keyring1_derive_key (const char *password, + gsize n_password, + GBytes *salt, + guint32 iteration_count); + +gboolean egg_keyring1_calculate_mac (GBytes *key, + const guint8 *value, + gsize n_value, + guint8 *buffer); + +gboolean egg_keyring1_verify_mac (GBytes *key, + const guint8 *value, + gsize n_value, + const guint8 *data); + +gboolean egg_keyring1_decrypt (GBytes *key, + guint8 *data, + gsize n_data); + +gboolean egg_keyring1_encrypt (GBytes *key, + guint8 *data, + gsize n_data); + +#endif /* EGG_KEYRING1_H_ */ diff --git a/egg/meson.build b/egg/meson.build index d2badac..cf88390 100644 --- a/egg/meson.build +++ b/egg/meson.build @@ -8,14 +8,27 @@ libegg_deps = [ glib_deps, ] -if get_option('gcrypt') +if with_crypto libegg_sources += [ 'egg-dh.c', - 'egg-hkdf.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') @@ -38,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 7befadd..3b171cf 100644 --- a/egg/test-dh.c +++ b/egg/test-dh.c @@ -33,86 +33,81 @@ #include #include -#include EGG_SECURE_DEFINE_GLIB_GLOBALS (); static void test_perform (void) { - gcry_mpi_t p, g; - gcry_mpi_t x1, X1; - gcry_mpi_t x2, X2; - gpointer k1, k2; + egg_dh_params *params; + egg_dh_pubkey *y1; + egg_dh_privkey *x1; + egg_dh_pubkey *y2; + egg_dh_privkey *x2; + GBytes *k1, *k2; gboolean ret; - gsize n1, n2; /* Load up the parameters */ - if (!egg_dh_default_params ("ietf-ike-grp-modp-768", &p, &g)) + params = egg_dh_default_params ("ietf-ike-grp-modp-768"); + if (!params) g_assert_not_reached (); /* Generate secrets */ - ret = egg_dh_gen_pair (p, g, 0, &X1, &x1); + ret = egg_dh_gen_pair (params, 0, &y1, &x1); g_assert_true (ret); - ret = egg_dh_gen_pair (p, g, 0, &X2, &x2); + ret = egg_dh_gen_pair (params, 0, &y2, &x2); g_assert_true (ret); /* Calculate keys */ - k1 = egg_dh_gen_secret (X2, x1, p, &n1); + k1 = egg_dh_gen_secret (y1, x2, params); g_assert_nonnull (k1); - k2 = egg_dh_gen_secret (X1, x2, p, &n2); + k2 = egg_dh_gen_secret (y2, x1, params); g_assert_nonnull (k2); /* Keys must be the same */ - egg_assert_cmpsize (n1, ==, n2); - g_assert_true (memcmp (k1, k2, n1) == 0); + g_assert_cmpmem (g_bytes_get_data (k1, NULL), g_bytes_get_size (k1), + g_bytes_get_data (k2, NULL), g_bytes_get_size (k2)); - gcry_mpi_release (p); - gcry_mpi_release (g); - gcry_mpi_release (x1); - gcry_mpi_release (X1); + egg_dh_params_free (params); + egg_dh_pubkey_free (y1); + egg_dh_privkey_free (x1); egg_secure_free (k1); - gcry_mpi_release (x2); - gcry_mpi_release (X2); + egg_dh_pubkey_free (y2); + egg_dh_privkey_free (x2); egg_secure_free (k2); } static void test_short_pair (void) { - gcry_mpi_t p, g; - gcry_mpi_t x1, X1; + egg_dh_params *params; + egg_dh_pubkey *y1; + egg_dh_privkey *x1; + GBytes *bytes; gboolean ret; /* Load up the parameters */ - ret = egg_dh_default_params ("ietf-ike-grp-modp-1024", &p, &g); - g_assert_true (ret); - g_assert_cmpuint (gcry_mpi_get_nbits (p), ==, 1024); + params = egg_dh_default_params ("ietf-ike-grp-modp-1024"); + g_assert_nonnull (params); /* Generate secrets */ - ret = egg_dh_gen_pair (p, g, 512, &X1, &x1); + ret = egg_dh_gen_pair (params, 512, &y1, &x1); g_assert_true (ret); - g_assert_cmpuint (gcry_mpi_get_nbits (x1), <=, 512); + bytes = egg_dh_pubkey_export (y1); + g_assert_cmpuint (g_bytes_get_size (bytes), <=, 512); + g_bytes_unref (bytes); - gcry_mpi_release (p); - gcry_mpi_release (g); - gcry_mpi_release (x1); - gcry_mpi_release (X1); + egg_dh_params_free (params); + egg_dh_pubkey_free (y1); + egg_dh_privkey_free (x1); } static void check_dh_default (const gchar *name, guint bits) { gboolean ret; - gcry_mpi_t p, g, check; gconstpointer prime, base; gsize n_prime, n_base; - gcry_error_t gcry; - - ret = egg_dh_default_params (name, &p, &g); - g_assert_true (ret); - g_assert_cmpint (gcry_mpi_get_nbits (p), ==, bits); - g_assert_cmpint (gcry_mpi_get_nbits (g), <, gcry_mpi_get_nbits (p)); ret = egg_dh_default_params_raw (name, &prime, &n_prime, &base, &n_base); g_assert_true (ret); @@ -120,19 +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); - g_assert_true (gcry_mpi_cmp (check, p) == 0); - gcry_mpi_release (check); - - gcry = gcry_mpi_scan (&check, GCRYMPI_FMT_USG, base, n_base, NULL); - g_assert_true (gcry == 0); - g_assert_true (gcry_mpi_cmp (check, g) == 0); - gcry_mpi_release (check); - - gcry_mpi_release (p); - gcry_mpi_release (g); } static void @@ -180,11 +162,10 @@ test_default_8192 (void) static void test_default_bad (void) { - gboolean ret; - gcry_mpi_t p, g; + egg_dh_params *params; - ret = egg_dh_default_params ("bad-name", &p, &g); - g_assert_false (ret); + params = egg_dh_default_params ("bad-name"); + g_assert_null (params); } int 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 96f8226..b33caad 100644 --- a/libsecret/secret-file-collection.c +++ b/libsecret/secret-file-collection.c @@ -16,26 +16,15 @@ #include "secret-file-collection.h" -#include "egg/egg-libgcrypt.h" +#include "egg/egg-keyring1.h" #include "egg/egg-secure-memory.h" EGG_SECURE_DECLARE (secret_file_collection); #ifdef WITH_GCRYPT -#include +#include "egg/egg-libgcrypt.h" #endif -#define PBKDF2_HASH_ALGO GCRY_MD_SHA256 -#define SALT_SIZE 32 -#define ITERATION_COUNT 100000 - -#define MAC_ALGO GCRY_MAC_HMAC_SHA256 -#define MAC_SIZE 32 - -#define CIPHER_ALGO GCRY_CIPHER_AES256 -#define CIPHER_BLOCK_SIZE 16 -#define IV_SIZE CIPHER_BLOCK_SIZE - #define KEYRING_FILE_HEADER "GnomeKeyring\n\r\0\n" #define KEYRING_FILE_HEADER_LEN 16 @@ -85,158 +74,6 @@ get_file_last_modified (SecretFileCollection *self) return res; } -static gboolean -do_derive_key (SecretFileCollection *self) -{ - const gchar *password; - gsize n_password; - gchar *key; - gsize n_salt; - gcry_error_t gcry; - - password = secret_value_get (self->password, &n_password); - - key = egg_secure_alloc (CIPHER_BLOCK_SIZE); - self->key = g_bytes_new_with_free_func (key, - CIPHER_BLOCK_SIZE, - egg_secure_free, - key); - - n_salt = g_bytes_get_size (self->salt); - gcry = gcry_kdf_derive (password, n_password, - GCRY_KDF_PBKDF2, PBKDF2_HASH_ALGO, - g_bytes_get_data (self->salt, NULL), n_salt, - self->iteration_count, CIPHER_BLOCK_SIZE, key); - return (gcry != 0) ? FALSE : TRUE; -} - -static gboolean -do_calculate_mac (SecretFileCollection *self, - const guint8 *value, gsize n_value, - guint8 *buffer) -{ - gcry_mac_hd_t hd; - gcry_error_t gcry; - gconstpointer secret; - gsize n_secret; - gboolean ret = FALSE; - - gcry = gcry_mac_open (&hd, MAC_ALGO, 0, NULL); - g_return_val_if_fail (gcry == 0, FALSE); - - secret = g_bytes_get_data (self->key, &n_secret); - gcry = gcry_mac_setkey (hd, secret, n_secret); - if (gcry != 0) - goto out; - - gcry = gcry_mac_write (hd, value, n_value); - if (gcry != 0) - goto out; - - n_value = MAC_SIZE; - gcry = gcry_mac_read (hd, buffer, &n_value); - if (gcry != 0) - goto out; - - if (n_value != MAC_SIZE) - goto out; - - ret = TRUE; - out: - gcry_mac_close (hd); - return ret; -} - -static gboolean -do_verify_mac (SecretFileCollection *self, - const guint8 *value, gsize n_value, - const guint8 *data) -{ - guint8 buffer[MAC_SIZE]; - guint8 status = 0; - gsize i; - - if (!do_calculate_mac (self, value, n_value, buffer)) { - return FALSE; - } - - for (i = 0; i < MAC_SIZE; i++) { - status |= data[i] ^ buffer[i]; - } - - return status == 0; -} - -static gboolean -do_decrypt (SecretFileCollection *self, - guint8 *data, - gsize n_data) -{ - gcry_cipher_hd_t hd; - gcry_error_t gcry; - gconstpointer secret; - gsize n_secret; - gboolean ret = FALSE; - - gcry = gcry_cipher_open (&hd, CIPHER_ALGO, GCRY_CIPHER_MODE_CBC, 0); - if (gcry != 0) - goto out; - - secret = g_bytes_get_data (self->key, &n_secret); - gcry = gcry_cipher_setkey (hd, secret, n_secret); - if (gcry != 0) - goto out; - - gcry = gcry_cipher_setiv (hd, data + n_data, IV_SIZE); - if (gcry != 0) - goto out; - - gcry = gcry_cipher_decrypt (hd, data, n_data, NULL, 0); - if (gcry != 0) - goto out; - - ret = TRUE; - out: - (void) gcry_cipher_close (hd); - return ret; -} - -static gboolean -do_encrypt (SecretFileCollection *self, - guint8 *data, - gsize n_data) -{ - gcry_cipher_hd_t hd; - gcry_error_t gcry; - gconstpointer secret; - gsize n_secret; - gboolean ret = FALSE; - - gcry = gcry_cipher_open (&hd, CIPHER_ALGO, GCRY_CIPHER_MODE_CBC, 0); - if (gcry != 0) - goto out; - - secret = g_bytes_get_data (self->key, &n_secret); - gcry = gcry_cipher_setkey (hd, secret, n_secret); - if (gcry != 0) - goto out; - - gcry_create_nonce (data + n_data, IV_SIZE); - - gcry = gcry_cipher_setiv (hd, data + n_data, IV_SIZE); - if (gcry != 0) - goto out; - - gcry = gcry_cipher_encrypt (hd, data, n_data, NULL, 0); - if (gcry != 0) - goto out; - - ret = TRUE; - out: - (void) gcry_cipher_close (hd); - return ret; -} - static void secret_file_collection_init (SecretFileCollection *self) { @@ -309,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 @@ -328,6 +166,8 @@ load_contents (SecretFileCollection *self, guint64 usage_count; gconstpointer data; gsize n_data; + const gchar *password; + gsize n_password; p = contents; if (length < KEYRING_FILE_HEADER_LEN || @@ -379,7 +219,12 @@ load_contents (SecretFileCollection *self, g_variant_unref (salt_array); g_variant_unref (variant); - if (!do_derive_key (self)) { + password = secret_value_get (self->password, &n_password); + self->key = egg_keyring1_derive_key (password, + n_password, + self->salt, + self->iteration_count); + if (!self->key) { g_set_error_literal (error, SECRET_ERROR, SECRET_ERROR_PROTOCOL, @@ -395,15 +240,22 @@ init_empty_file (SecretFileCollection *self, GError **error) { GVariantBuilder builder; + const gchar *password; + gsize n_password; guint8 salt[SALT_SIZE]; - gcry_create_nonce (salt, sizeof(salt)); + egg_keyring1_create_nonce (salt, sizeof(salt)); self->salt = g_bytes_new (salt, sizeof(salt)); self->iteration_count = ITERATION_COUNT; self->modified = g_date_time_new_now_utc (); self->usage_count = 0; - if (!do_derive_key (self)) { + password = secret_value_get (self->password, &n_password); + self->key = egg_keyring1_derive_key (password, + n_password, + self->salt, + self->iteration_count); + if (!self->key) { g_set_error_literal (error, SECRET_ERROR, SECRET_ERROR_PROTOCOL, @@ -555,7 +407,10 @@ hash_attributes (SecretFileCollection *self, GVariant *variant; value = g_hash_table_lookup (attributes, l->data); - if (!do_calculate_mac (self, (guint8 *)value, strlen (value), buffer)) { + if (!egg_keyring1_calculate_mac (self->key, + (const guint8 *)value, + strlen (value), + buffer)) { g_list_free (keys); return NULL; } @@ -597,7 +452,7 @@ hashed_attributes_match (SecretFileCollection *self, return FALSE; } - if (!do_verify_mac (self, value, strlen ((char *)value), data)) { + if (!egg_keyring1_verify_mac (self->key, value, strlen ((char *)value), data)) { g_variant_unref (hashed_attribute); return FALSE; } @@ -693,7 +548,7 @@ secret_file_collection_replace (SecretFileCollection *self, g_variant_store (serialized_item, data); g_variant_unref (serialized_item); memset (data + n_data, n_padded - n_data, n_padded - n_data); - if (!do_encrypt (self, data, n_padded)) { + if (!egg_keyring1_encrypt (self->key, data, n_padded)) { egg_secure_free (data); g_set_error (error, SECRET_ERROR, @@ -702,8 +557,8 @@ secret_file_collection_replace (SecretFileCollection *self, return FALSE; } - if (!do_calculate_mac (self, data, n_padded + IV_SIZE, - data + n_padded + IV_SIZE)) { + if (!egg_keyring1_calculate_mac (self->key, data, n_padded + IV_SIZE, + data + n_padded + IV_SIZE)) { egg_secure_free (data); g_set_error (error, SECRET_ERROR, @@ -791,7 +646,7 @@ _secret_file_item_decrypt (GVariant *encrypted, } n_padded -= MAC_SIZE; - if (!do_verify_mac (collection, data, n_padded, data + n_padded)) { + if (!egg_keyring1_verify_mac (collection->key, data, n_padded, data + n_padded)) { egg_secure_free (data); g_set_error (error, SECRET_ERROR, @@ -801,7 +656,7 @@ _secret_file_item_decrypt (GVariant *encrypted, } n_padded -= IV_SIZE; - if (!do_decrypt (collection, data, n_padded)) { + if (!egg_keyring1_decrypt (collection->key, data, n_padded)) { egg_secure_free (data); g_set_error (error, SECRET_ERROR, diff --git a/libsecret/secret-session.c b/libsecret/secret-session.c index 7ca77ee..375984c 100644 --- a/libsecret/secret-session.c +++ b/libsecret/secret-session.c @@ -17,10 +17,18 @@ #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" @@ -36,10 +44,10 @@ EGG_SECURE_DECLARE (secret_session); struct _SecretSession { gchar *path; const gchar *algorithms; -#ifdef WITH_GCRYPT - gcry_mpi_t prime; - gcry_mpi_t privat; - gcry_mpi_t publi; +#ifdef WITH_CRYPTO + egg_dh_params *params; + egg_dh_privkey *privat; + egg_dh_pubkey *publi; #endif gpointer key; gsize n_key; @@ -54,55 +62,52 @@ _secret_session_free (gpointer data) return; g_free (session->path); -#ifdef WITH_GCRYPT - gcry_mpi_release (session->publi); - gcry_mpi_release (session->privat); - gcry_mpi_release (session->prime); +#ifdef WITH_CRYPTO + egg_dh_pubkey_free (session->publi); + egg_dh_privkey_free (session->privat); + egg_dh_params_free (session->params); #endif egg_secure_free (session->key); g_free (session); } -#ifdef WITH_GCRYPT +#ifdef WITH_CRYPTO static GVariant * request_open_session_aes (SecretSession *session) { - gcry_error_t gcry; - gcry_mpi_t base; - unsigned char *buffer; - size_t n_buffer; + GBytes *buffer; GVariant *argument; - g_assert (session->prime == NULL); + g_assert (session->params == NULL); g_assert (session->privat == NULL); g_assert (session->publi == NULL); +#ifdef WITH_GCRYPT egg_libgcrypt_initialize (); +#endif /* Initialize our local parameters and values */ - if (!egg_dh_default_params ("ietf-ike-grp-modp-1024", - &session->prime, &base)) + session->params = egg_dh_default_params ("ietf-ike-grp-modp-1024"); + if (!session->params) g_return_val_if_reached (NULL); #if 0 - g_printerr ("\n lib prime: "); - gcry_mpi_dump (session->prime); - g_printerr ("\n lib base: "); - gcry_mpi_dump (base); + g_printerr ("\n lib params: "); + egg_dh_params_dump (session->params); g_printerr ("\n"); #endif - if (!egg_dh_gen_pair (session->prime, base, 0, + if (!egg_dh_gen_pair (session->params, 0, &session->publi, &session->privat)) g_return_val_if_reached (NULL); - gcry_mpi_release (base); - gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, session->publi); - g_return_val_if_fail (gcry == 0, NULL); - argument = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), - buffer, n_buffer, TRUE, - gcry_free, buffer); + buffer = egg_dh_pubkey_export (session->publi); + g_return_val_if_fail (buffer != NULL, NULL); + argument = g_variant_new_from_bytes (G_VARIANT_TYPE ("ay"), + buffer, + TRUE); + g_bytes_unref (buffer); return g_variant_new ("(sv)", ALGORITHMS_AES, argument); } @@ -111,14 +116,11 @@ static gboolean response_open_session_aes (SecretSession *session, GVariant *response) { - gconstpointer buffer; + GBytes *buffer; GVariant *argument; const gchar *sig; - gsize n_buffer; - gcry_mpi_t peer; - gcry_error_t gcry; - gpointer ikm; - gsize n_ikm; + egg_dh_pubkey *peer; + GBytes *ikm; sig = g_variant_get_type_string (response); g_return_val_if_fail (sig != NULL, FALSE); @@ -131,24 +133,27 @@ response_open_session_aes (SecretSession *session, g_assert (session->path == NULL); g_variant_get (response, "(vo)", &argument, &session->path); - buffer = g_variant_get_fixed_array (argument, &n_buffer, sizeof (guchar)); - gcry = gcry_mpi_scan (&peer, GCRYMPI_FMT_USG, buffer, n_buffer, NULL); - g_return_val_if_fail (gcry == 0, FALSE); + buffer = g_variant_get_data_as_bytes (argument); + peer = egg_dh_pubkey_new_from_bytes (session->params, buffer); + g_bytes_unref (buffer); + g_return_val_if_fail (peer != NULL, FALSE); g_variant_unref (argument); #if 0 g_printerr (" lib publi: "); - gcry_mpi_dump (session->publi); + egg_dh_pubkey_dump (session->publi); g_printerr ("\n lib peer: "); - gcry_mpi_dump (peer); + egg_dh_pubkey_dump (peer); g_printerr ("\n"); #endif - ikm = egg_dh_gen_secret (peer, session->privat, session->prime, &n_ikm); - gcry_mpi_release (peer); + ikm = egg_dh_gen_secret (peer, session->privat, session->params); + egg_dh_pubkey_free (peer); #if 0 - g_printerr (" lib ikm: %s\n", egg_hex_encode (ikm, n_ikm)); + g_printerr (" lib ikm: %s\n", + egg_hex_encode (g_bytes_get_data (ikm, NULL), + g_bytes_get_size (ikm))); #endif if (ikm == NULL) { @@ -160,16 +165,19 @@ response_open_session_aes (SecretSession *session, session->n_key = 16; session->key = egg_secure_alloc (session->n_key); - if (!egg_hkdf_perform ("sha256", ikm, n_ikm, NULL, 0, NULL, 0, + if (!egg_hkdf_perform ("sha256", + g_bytes_get_data (ikm, NULL), + g_bytes_get_size (ikm), + NULL, 0, NULL, 0, session->key, session->n_key)) g_return_val_if_reached (FALSE); - egg_secure_free (ikm); + g_bytes_unref (ikm); session->algorithms = ALGORITHMS_AES; return TRUE; } -#endif /* WITH_GCRYPT */ +#endif /* WITH_CRYPTO */ static GVariant * request_open_session_plain (SecretSession *session) @@ -252,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, @@ -301,7 +309,7 @@ on_service_open_session_aes (GObject *source, g_object_unref (task); } -#endif /* WITH_GCRYPT */ +#endif /* WITH_CRYPTO */ void @@ -320,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, @@ -346,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, @@ -378,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, @@ -448,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, @@ -497,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); @@ -514,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, @@ -536,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, @@ -595,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, @@ -634,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 8e993ee..d0ad881 100644 --- a/libsecret/test-session.c +++ b/libsecret/test-session.c @@ -29,6 +29,12 @@ #include #include +#ifdef WITH_CRYPTO +#define SESSION_ALGO "dh-ietf1024-sha256-aes128-cbc-pkcs7" +#else +#define SESSION_ALGO "plain" +#endif + typedef struct { SecretService *service; } Test; @@ -73,7 +79,7 @@ test_ensure (Test *test, g_assert_no_error (error); g_assert_true (ret); g_assert_cmpstr (secret_service_get_session_dbus_path (test->service), !=, NULL); - g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, "dh-ietf1024-sha256-aes128-cbc-pkcs7"); + g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, SESSION_ALGO); } static void @@ -91,14 +97,14 @@ test_ensure_twice (Test *test, g_assert_no_error (error); g_assert_true (ret); g_assert_cmpstr (secret_service_get_session_dbus_path (test->service), !=, NULL); - g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, "dh-ietf1024-sha256-aes128-cbc-pkcs7"); + g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, SESSION_ALGO); path = g_strdup (secret_service_get_session_dbus_path (test->service)); ret = secret_service_ensure_session_sync (test->service, NULL, &error); g_assert_no_error (error); g_assert_true (ret); g_assert_cmpstr (secret_service_get_session_dbus_path (test->service), ==, path); - g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, "dh-ietf1024-sha256-aes128-cbc-pkcs7"); + g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, SESSION_ALGO); g_free (path); } @@ -172,7 +178,7 @@ test_ensure_async_aes (Test *test, g_assert_true (ret); g_assert_cmpstr (secret_service_get_session_dbus_path (test->service), !=, NULL); - g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, "dh-ietf1024-sha256-aes128-cbc-pkcs7"); + g_assert_cmpstr (secret_service_get_session_algorithms (test->service), ==, SESSION_ALGO); g_object_unref (result); } 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,