mirror of
https://gitlab.gnome.org/GNOME/libsecret.git
synced 2024-12-22 04:38:55 +00:00
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 <dueno@src.gnome.org>
This commit is contained in:
parent
0b4769f871
commit
564874beb0
193
egg/egg-keyring1.c
Normal file
193
egg/egg-keyring1.c
Normal file
@ -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 <gcrypt.h>
|
||||
|
||||
#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;
|
||||
}
|
55
egg/egg-keyring1.h
Normal file
55
egg/egg-keyring1.h
Normal file
@ -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 <glib.h>
|
||||
|
||||
#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_ */
|
@ -12,6 +12,7 @@ if get_option('gcrypt')
|
||||
libegg_sources += [
|
||||
'egg-dh.c',
|
||||
'egg-hkdf.c',
|
||||
'egg-keyring1.c',
|
||||
'egg-libgcrypt.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 <gcrypt.h>
|
||||
#endif
|
||||
|
||||
#define PBKDF2_HASH_ALGO GCRY_MD_SHA256
|
||||
#define SALT_SIZE 32
|
||||
#define ITERATION_COUNT 100000
|
||||
|
||||
#define MAC_ALGO GCRY_MAC_HMAC_SHA256
|
||||
#define MAC_SIZE 32
|
||||
|
||||
#define CIPHER_ALGO GCRY_CIPHER_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,
|
||||
|
Loading…
Reference in New Issue
Block a user