From 564874beb07e3e328805f02b1a4b163e7ee1946e Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 1 Dec 2023 09:36:02 +0900 Subject: [PATCH] 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,