From 47f524e2df3fe941a16012e8c56b878eddd86de4 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Sat, 9 Sep 2023 12:18:14 +0900 Subject: [PATCH 1/6] egg-dh: Hide gcry_mpi_t from the internal API This wraps gcry_mpi_t usage in the API with opaque strucs, so it would be easier to port egg-dh to other crypto libraries. Signed-off-by: Daiki Ueno --- egg/egg-dh.c | 212 +++++++++++++++++++++++++++++-------- egg/egg-dh.h | 42 ++++---- egg/test-dh.c | 80 +++++++------- libsecret/secret-session.c | 79 +++++++------- 4 files changed, 265 insertions(+), 148 deletions(-) diff --git a/egg/egg-dh.c b/egg/egg-dh.c index b190488..d3d66b3 100644 --- a/egg/egg-dh.c +++ b/egg/egg-dh.c @@ -26,6 +26,8 @@ #include "egg-dh.h" #include "egg-secure-memory.h" +#include + /* Enabling this is a complete security compromise */ #define DEBUG_DH_SECRET 0 @@ -212,6 +214,19 @@ 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) @@ -237,44 +252,61 @@ 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) +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, FALSE); + g_return_val_if_fail (name, NULL); - 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; - } - } + for (group = dh_groups; group->name; ++group) + if (g_str_equal (group->name, name)) + break; + if (!group->name) + return NULL; - return FALSE; + 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 (gcry_mpi_t prime, gcry_mpi_t base, guint bits, - gcry_mpi_t *pub, gcry_mpi_t *priv) +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 (prime, FALSE); - g_return_val_if_fail (base, FALSE); + g_return_val_if_fail (params, FALSE); g_return_val_if_fail (pub, FALSE); g_return_val_if_fail (priv, FALSE); - pbits = gcry_mpi_get_nbits (prime); + *pub = NULL; + *priv = NULL; + + pbits = gcry_mpi_get_nbits (params->prime); g_return_val_if_fail (pbits > 1, FALSE); if (bits == 0) { @@ -289,28 +321,47 @@ egg_dh_gen_pair (gcry_mpi_t prime, gcry_mpi_t base, guint bits, * 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); + 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) > 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); + 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 = gcry_mpi_new (gcry_mpi_get_nbits (*priv)); - g_return_val_if_fail (*pub, FALSE); - gcry_mpi_powm (*pub, base, *priv, prime); + 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); } -gpointer -egg_dh_gen_secret (gcry_mpi_t peer, gcry_mpi_t priv, - gcry_mpi_t prime, gsize *bytes) +GBytes * +egg_dh_gen_secret (egg_dh_pubkey *peer, egg_dh_privkey *priv, + egg_dh_params *params) { gcry_error_t gcry; guchar *value; @@ -321,17 +372,17 @@ egg_dh_gen_secret (gcry_mpi_t peer, gcry_mpi_t priv, g_return_val_if_fail (peer, NULL); g_return_val_if_fail (priv, NULL); - g_return_val_if_fail (prime, NULL); + g_return_val_if_fail (params, NULL); - bits = gcry_mpi_get_nbits (prime); + 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, priv, prime); + 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, prime); + 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); @@ -349,14 +400,83 @@ egg_dh_gen_secret (gcry_mpi_t peer, gcry_mpi_t priv, #endif gcry_mpi_release (k); - *bytes = n_prime; - #if DEBUG_DH_SECRET - gcry_mpi_scan (&k, GCRYMPI_FMT_USG, value, bytes, NULL); + 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 value; + 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 679aa19..0883ec1 100644 --- a/egg/egg-dh.h +++ b/egg/egg-dh.h @@ -26,27 +26,33 @@ #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); +egg_dh_params *egg_dh_default_params (const gchar *name); -gboolean egg_dh_default_params_raw (const gchar *name, - gconstpointer *prime, - gsize *n_prime, - gconstpointer *base, - gsize *n_base); +gboolean egg_dh_default_params_raw (const gchar *name, + gconstpointer *prime, + gsize *n_prime, + gconstpointer *base, + gsize *n_base); -gboolean egg_dh_gen_pair (gcry_mpi_t prime, - gcry_mpi_t base, - guint bits, - gcry_mpi_t *pub, - gcry_mpi_t *priv); +gboolean egg_dh_gen_pair (egg_dh_params *params, + guint bits, + egg_dh_pubkey **pub, + egg_dh_privkey **priv); -gpointer egg_dh_gen_secret (gcry_mpi_t peer, - gcry_mpi_t priv, - gcry_mpi_t prime, - gsize *bytes); +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/test-dh.c b/egg/test-dh.c index 7befadd..05831c0 100644 --- a/egg/test-dh.c +++ b/egg/test-dh.c @@ -40,80 +40,78 @@ 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; + gcry_mpi_t 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); g_assert_nonnull (prime); @@ -123,16 +121,11 @@ check_dh_default (const gchar *name, guint bits) 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 +173,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/secret-session.c b/libsecret/secret-session.c index 7ca77ee..0fecbff 100644 --- a/libsecret/secret-session.c +++ b/libsecret/secret-session.c @@ -21,6 +21,7 @@ #include "egg/egg-dh.h" #include "egg/egg-hkdf.h" #include "egg/egg-libgcrypt.h" +#include #endif #include "egg/egg-hex.h" @@ -37,9 +38,9 @@ struct _SecretSession { gchar *path; const gchar *algorithms; #ifdef WITH_GCRYPT - gcry_mpi_t prime; - gcry_mpi_t privat; - gcry_mpi_t publi; + egg_dh_params *params; + egg_dh_privkey *privat; + egg_dh_pubkey *publi; #endif gpointer key; gsize n_key; @@ -55,9 +56,9 @@ _secret_session_free (gpointer data) g_free (session->path); #ifdef WITH_GCRYPT - gcry_mpi_release (session->publi); - gcry_mpi_release (session->privat); - gcry_mpi_release (session->prime); + 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); @@ -68,41 +69,36 @@ _secret_session_free (gpointer data) 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); egg_libgcrypt_initialize (); /* 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 +107,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 +124,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,10 +156,13 @@ 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; From 0b4769f871968137be847815ceeaf725c8322282 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Tue, 28 Nov 2023 14:56:19 +0900 Subject: [PATCH 2/6] file-collection: Make it consistent about block cipher algorithm The original code tries to encrypt the file format using AES-256-CBC, though actually AES-128-CBC was used because the key size is shorter and libgcrypt automatically degrades to AES-128-CBC based on the key size. Reported by Sophie Herold in: https://github.com/bilelmoussaoui/oo7/issues/46#issuecomment-1816286494 Signed-off-by: Daiki Ueno --- libsecret/secret-file-collection.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libsecret/secret-file-collection.c b/libsecret/secret-file-collection.c index 96f8226..90c20fc 100644 --- a/libsecret/secret-file-collection.c +++ b/libsecret/secret-file-collection.c @@ -32,8 +32,9 @@ EGG_SECURE_DECLARE (secret_file_collection); #define MAC_ALGO GCRY_MAC_HMAC_SHA256 #define MAC_SIZE 32 -#define CIPHER_ALGO GCRY_CIPHER_AES256 +#define CIPHER_ALGO GCRY_CIPHER_AES128 #define CIPHER_BLOCK_SIZE 16 +#define KEY_SIZE 16 #define IV_SIZE CIPHER_BLOCK_SIZE #define KEYRING_FILE_HEADER "GnomeKeyring\n\r\0\n" @@ -96,9 +97,9 @@ do_derive_key (SecretFileCollection *self) password = secret_value_get (self->password, &n_password); - key = egg_secure_alloc (CIPHER_BLOCK_SIZE); + key = egg_secure_alloc (KEY_SIZE); self->key = g_bytes_new_with_free_func (key, - CIPHER_BLOCK_SIZE, + KEY_SIZE, egg_secure_free, key); @@ -106,7 +107,7 @@ do_derive_key (SecretFileCollection *self) 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); + self->iteration_count, KEY_SIZE, key); return (gcry != 0) ? FALSE : TRUE; } From 564874beb07e3e328805f02b1a4b163e7ee1946e Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 1 Dec 2023 09:36:02 +0900 Subject: [PATCH 3/6] file-collection: Move low-level crypto functions to egg This moves low-level cryptographic functions into egg/egg-keyring1.c, to make it easy to support multiple crypto backend libraries. Signed-off-by: Daiki Ueno --- egg/egg-keyring1.c | 193 +++++++++++++++++++++++++++ egg/egg-keyring1.h | 55 ++++++++ egg/meson.build | 1 + libsecret/secret-file-collection.c | 206 ++++------------------------- 4 files changed, 277 insertions(+), 178 deletions(-) create mode 100644 egg/egg-keyring1.c create mode 100644 egg/egg-keyring1.h diff --git a/egg/egg-keyring1.c b/egg/egg-keyring1.c new file mode 100644 index 0000000..bb987be --- /dev/null +++ b/egg/egg-keyring1.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..04a305c 100644 --- a/egg/meson.build +++ b/egg/meson.build @@ -12,6 +12,7 @@ if get_option('gcrypt') libegg_sources += [ 'egg-dh.c', 'egg-hkdf.c', + 'egg-keyring1.c', 'egg-libgcrypt.c', ] diff --git a/libsecret/secret-file-collection.c b/libsecret/secret-file-collection.c index 90c20fc..bc2b600 100644 --- a/libsecret/secret-file-collection.c +++ b/libsecret/secret-file-collection.c @@ -16,27 +16,12 @@ #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 -#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_AES128 -#define CIPHER_BLOCK_SIZE 16 -#define KEY_SIZE 16 -#define IV_SIZE CIPHER_BLOCK_SIZE - #define KEYRING_FILE_HEADER "GnomeKeyring\n\r\0\n" #define KEYRING_FILE_HEADER_LEN 16 @@ -86,158 +71,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 (KEY_SIZE); - self->key = g_bytes_new_with_free_func (key, - KEY_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, KEY_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) { @@ -329,6 +162,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 || @@ -380,7 +215,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, @@ -396,15 +236,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, @@ -556,7 +403,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; } @@ -598,7 +448,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; } @@ -694,7 +544,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, @@ -703,8 +553,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, @@ -792,7 +642,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, @@ -802,7 +652,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, From 0f49b34fa2b20dee49a78f362fd3fae546077be4 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Wed, 29 Nov 2023 06:44:32 +0900 Subject: [PATCH 4/6] tests: Conditionalize session algorithm check in test-session When compiled without libgcrypt, some of the tests in test-session fails as they expect the session algorithm to be "dh-ietf1024-sha256-aes128-cbc-pkcs7". This adds a build-time conditional to guard against it. Signed-off-by: Daiki Ueno --- libsecret/test-session.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libsecret/test-session.c b/libsecret/test-session.c index 8e993ee..c94f2fa 100644 --- a/libsecret/test-session.c +++ b/libsecret/test-session.c @@ -29,6 +29,12 @@ #include #include +#ifdef WITH_GCRYPT +#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); } From 28486191b2d2cf1599cd3c051b304fac927e24cf Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Sun, 10 Sep 2023 11:42:06 +0900 Subject: [PATCH 5/6] 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, From b3f5823d1246ca7a59f0bdf7f23c510f5029d907 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Tue, 28 Nov 2023 08:20:44 +0900 Subject: [PATCH 6/6] .gitlab-ci.yml: Exercise both libgcrypt and gnutls crypto backends Note that gnutls 3.8.2 packages is still under testing, so we tentatively pull in the build directly from koji. Signed-off-by: Daiki Ueno --- .gitlab-ci.yml | 25 ++++++++++++++++++++----- .gitlab-ci/master.Dockerfile | 3 ++- 2 files changed, 22 insertions(+), 6 deletions(-) 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 \