summaryrefslogtreecommitdiff
path: root/src/libostree/ostree-sign-ed25519.c
diff options
context:
space:
mode:
authorDenis Pynkin <denis.pynkin@collabora.com>2019-10-27 23:15:10 +0300
committerDenis Pynkin <denis.pynkin@collabora.com>2020-03-25 15:23:54 +0300
commitee12b7e7743d695c06c1ffbe1d76bcb53975f83b (patch)
tree55cc21f5f5d500059cb3ce1b6ffce77eb12bcc65 /src/libostree/ostree-sign-ed25519.c
parentceaf6d7f546690601df98ee5e9d971f5b31096a9 (diff)
downloadostree-ee12b7e7743d695c06c1ffbe1d76bcb53975f83b.tar.gz
lib/sign: add revoking mechanism for ed25519 keys
Skip public keys verification if key is marked as invalid key. Allow to redefine system-wide directories for ed25519 verification. Minor bugfixes. Signed-off-by: Denis Pynkin <denis.pynkin@collabora.com>
Diffstat (limited to 'src/libostree/ostree-sign-ed25519.c')
-rw-r--r--src/libostree/ostree-sign-ed25519.c254
1 files changed, 194 insertions, 60 deletions
diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c
index f61c3bdd..0c7cd951 100644
--- a/src/libostree/ostree-sign-ed25519.c
+++ b/src/libostree/ostree-sign-ed25519.c
@@ -44,6 +44,7 @@ struct _OstreeSignEd25519
gboolean initialized;
guchar *secret_key;
GList *public_keys;
+ GList *revoked_keys;
};
static void
@@ -83,6 +84,7 @@ ostree_sign_ed25519_init (OstreeSignEd25519 *self)
self->initialized = TRUE;
self->secret_key = NULL;
self->public_keys = NULL;
+ self->revoked_keys = NULL;
#ifdef HAVE_LIBSODIUM
if (sodium_init() < 0)
@@ -139,6 +141,13 @@ err:
return FALSE;
}
+#ifdef HAVE_LIBSODIUM
+static gint
+_compare_ed25519_keys(gconstpointer a, gconstpointer b) {
+ return memcmp (a, b, crypto_sign_PUBLICKEYBYTES);
+}
+#endif
+
gboolean ostree_sign_ed25519_data_verify (OstreeSign *self,
GBytes *data,
GVariant *signatures,
@@ -204,6 +213,15 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self,
public_key != NULL;
public_key = public_key->next)
{
+
+ /* TODO: use non-list for tons of revoked keys? */
+ if (g_list_find_custom (sign->revoked_keys, public_key->data, _compare_ed25519_keys) != NULL)
+ {
+ g_debug("Skip revoked key '%s'",
+ sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, public_key->data, crypto_sign_PUBLICKEYBYTES));
+ continue;
+ }
+
if (crypto_sign_verify_detached ((guchar *) g_variant_get_data (child),
g_bytes_get_data (data, NULL),
g_bytes_get_size (data),
@@ -278,6 +296,13 @@ gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self,
sign->public_keys = NULL;
}
+ /* Clear already loaded revoked keys */
+ if (sign->revoked_keys != NULL)
+ {
+ g_list_free_full (sign->revoked_keys, g_free);
+ sign->revoked_keys = NULL;
+ }
+
return TRUE;
#endif /* HAVE_LIBSODIUM */
@@ -344,8 +369,6 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self,
g_debug ("%s enter", __FUNCTION__);
g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE);
- OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self));
-
ostree_sign_ed25519_clear_keys (self, error);
return ostree_sign_ed25519_add_pk (self, public_key, error);
@@ -365,7 +388,7 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self,
#ifdef HAVE_LIBSODIUM
OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self));
g_autofree char * hex = NULL;
- gpointer key = NULL;
+ gpointer key = NULL;
gsize n_elements = 0;
@@ -395,7 +418,7 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self,
goto err;
}
- if (g_list_find (sign->public_keys, key) == NULL)
+ if (g_list_find_custom (sign->public_keys, key, _compare_ed25519_keys) == NULL)
{
gpointer newkey = g_memdup (key, n_elements);
sign->public_keys = g_list_prepend (sign->public_keys, newkey);
@@ -408,10 +431,65 @@ err:
return FALSE;
}
+#ifdef HAVE_LIBSODIUM
+/* Add revoked public key */
+static gboolean
+_ed25519_add_revoked (OstreeSign *self,
+ GVariant *revoked_key,
+ GError **error)
+{
+ g_debug ("%s enter", __FUNCTION__);
+ g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE);
+
+ OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self));
+ g_autofree char * hex = NULL;
+ gpointer key = NULL;
+
+ gsize n_elements = 0;
+
+ if (g_variant_is_of_type (revoked_key, G_VARIANT_TYPE_STRING))
+ {
+ const gchar *rk_ascii = g_variant_get_string (revoked_key, NULL);
+ key = g_base64_decode (rk_ascii, &n_elements);
+ }
+ else
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Unknown ed25519 revoked key type");
+ goto err;
+ }
+
+ hex = g_malloc0 (crypto_sign_PUBLICKEYBYTES*2 + 1);
+ g_debug ("Read ed25519 revoked key = %s", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, key, n_elements));
+
+ if (n_elements != crypto_sign_PUBLICKEYBYTES)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Incorrect ed25519 revoked key");
+ goto err;
+ }
+
+ if (g_list_find_custom (sign->revoked_keys, key, _compare_ed25519_keys) == NULL)
+ {
+ gpointer newkey = g_memdup (key, n_elements);
+ sign->revoked_keys = g_list_prepend (sign->revoked_keys, newkey);
+ }
+
+ return TRUE;
+
+err:
+ return FALSE;
+}
+#endif /* HAVE_LIBSODIUM */
+
static gboolean
-_load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, GError **error)
+_load_pk_from_stream (OstreeSign *self,
+ GDataInputStream *key_data_in,
+ gboolean trusted,
+ GError **error)
{
+ g_debug ("%s enter", __FUNCTION__);
g_return_val_if_fail (key_data_in, FALSE);
#ifdef HAVE_LIBSODIUM
gboolean ret = FALSE;
@@ -422,23 +500,31 @@ _load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, GError **
gsize len = 0;
g_autofree char *line = g_data_input_stream_read_line (key_data_in, &len, NULL, error);
g_autoptr (GVariant) pk = NULL;
+ gboolean added = FALSE;
if (*error != NULL)
goto err;
if (line == NULL)
- goto out;
+ goto out;
/* Read the key itself */
/* base64 encoded key */
pk = g_variant_new_string (line);
- if (ostree_sign_ed25519_add_pk (self, pk, error))
- {
- ret = TRUE;
- g_debug ("Added public key: %s", line);
- }
+
+ if (trusted)
+ added = ostree_sign_ed25519_add_pk (self, pk, error);
else
- g_debug ("Invalid public key: %s", line);
+ added = _ed25519_add_revoked (self, pk, error);
+
+ g_debug ("%s %s key: %s",
+ added ? "Added" : "Invalid",
+ trusted ? "public" : "revoked",
+ line);
+
+ /* Mark what we load at least one key */
+ if (added)
+ ret = TRUE;
}
out:
@@ -452,6 +538,7 @@ err:
static gboolean
_load_pk_from_file (OstreeSign *self,
const gchar *filename,
+ gboolean trusted,
GError **error)
{
g_debug ("%s enter", __FUNCTION__);
@@ -477,64 +564,63 @@ _load_pk_from_file (OstreeSign *self,
key_data_in = g_data_input_stream_new (G_INPUT_STREAM(key_stream_in));
g_assert (key_data_in != NULL);
- if (!_load_pk_from_stream (self, key_data_in, error))
- goto err;
+ if (!_load_pk_from_stream (self, key_data_in, trusted, error))
+ {
+ if (error == NULL || *error == NULL)
+ g_set_error (error,
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ "signature: ed25519: no valid keys in file '%s'",
+ filename);
+ goto err;
+ }
return TRUE;
err:
return FALSE;
}
-gboolean
-ostree_sign_ed25519_load_pk (OstreeSign *self,
- GVariant *options,
- GError **error)
+static gboolean
+_ed25519_load_pk (OstreeSign *self,
+ GVariant *options,
+ gboolean trusted,
+ GError **error)
{
g_debug ("%s enter", __FUNCTION__);
gboolean ret = FALSE;
+ const gchar *custom_dir = NULL;
- /* Default paths there to find files with public keys */
- const gchar *default_dirs[] =
+ g_autoptr (GPtrArray) base_dirs = g_ptr_array_new_with_free_func (g_free);
+ g_autoptr (GPtrArray) ed25519_files = g_ptr_array_new_with_free_func (g_free);
+
+ if (g_variant_lookup (options, "basedir", "&s", &custom_dir))
{
- "/etc/ostree/trusted.ed25519.d",
- DATADIR "/ostree/trusted.ed25519.d"
- };
- const gchar *default_files[] =
+ /* Add custom directory */
+ g_ptr_array_add (base_dirs, g_strdup (custom_dir));
+ }
+ else
{
- "/etc/ostree/trusted.ed25519",
- DATADIR "/ostree/trusted.ed25519"
- };
-
- OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self));
-
- const gchar *filename = NULL;
+ /* Default paths where to find files with public keys */
+ g_ptr_array_add (base_dirs, g_strdup ("/etc/ostree"));
+ g_ptr_array_add (base_dirs, g_strdup (DATADIR "/ostree"));
+ }
- /* Clear already loaded keys */
- if (sign->public_keys != NULL)
+ /* Scan all well-known directories and construct the list with file names to scan keys */
+ for (gint i=0; i < base_dirs->len; i++)
{
- g_list_free_full (sign->public_keys, g_free);
- sign->public_keys = NULL;
- }
+ gchar *base_name = NULL;
+ g_autofree gchar *base_dir = NULL;
+ g_autoptr (GDir) dir = NULL;
- /* Read only file provided */
- if (g_variant_lookup (options, "filename", "&s", &filename))
- return _load_pk_from_file (self, filename, error);
+ base_name = g_build_filename ((gchar *)g_ptr_array_index (base_dirs, i),
+ trusted ? "trusted.ed25519" : "revoked.ed25519",
+ NULL);
- /* Scan all well-known files and directories */
- for (gint i=0; i < G_N_ELEMENTS(default_files); i++)
- if (!_load_pk_from_file (self, default_files[i], error))
- {
- g_debug ("Problem with loading ed25519 public keys from `%s`", default_files[i]);
- g_clear_error(error);
- }
- else
- ret = TRUE;
+ g_debug ("Check ed25519 keys from file: %s", base_name);
+ g_ptr_array_add (ed25519_files, base_name);
- /* Scan all well-known files and directories */
- for (gint i=0; i < G_N_ELEMENTS(default_dirs); i++)
- {
- g_autoptr (GDir) dir = g_dir_open (default_dirs[i], 0, error);
+ base_dir = g_strconcat (base_name, ".d", NULL);
+ dir = g_dir_open (base_dir, 0, error);
if (dir == NULL)
{
g_clear_error (error);
@@ -543,17 +629,65 @@ ostree_sign_ed25519_load_pk (OstreeSign *self,
const gchar *entry = NULL;
while ((entry = g_dir_read_name (dir)) != NULL)
{
- filename = g_build_filename (default_dirs[i], entry, NULL);
- if (!_load_pk_from_file (self, filename, error))
- {
- g_debug ("Problem with loading ed25519 public keys from `%s`", filename);
- g_clear_error(error);
- }
- else
- ret = TRUE;
+ gchar *filename = g_build_filename (base_dir, entry, NULL);
+ g_debug ("Check ed25519 keys from file: %s", filename);
+ g_ptr_array_add (ed25519_files, filename);
}
}
+ /* Scan all well-known files */
+ for (gint i=0; i < ed25519_files->len; i++)
+ {
+ if (!_load_pk_from_file (self, (gchar *)g_ptr_array_index (ed25519_files, i), trusted, error))
+ {
+ g_debug ("Problem with loading ed25519 %s keys from `%s`",
+ trusted ? "public" : "revoked",
+ (gchar *)g_ptr_array_index (ed25519_files, i));
+ g_clear_error(error);
+ }
+ else
+ ret = TRUE;
+ }
+
+ if (!ret && (error == NULL || *error == NULL))
+ g_set_error_literal (error,
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ "signature: ed25519: no keys loaded");
+
return ret;
}
+/*
+ * options argument should be a{sv}:
+ * - filename -- single file to use to load keys from;
+ * - basedir -- directory containing subdirectories
+ * 'trusted.ed25519.d' and 'revoked.ed25519.d' with appropriate
+ * public keys. Used for testing and re-definition of system-wide
+ * directories if defaults are not suitable for any reason.
+ */
+gboolean
+ostree_sign_ed25519_load_pk (OstreeSign *self,
+ GVariant *options,
+ GError **error)
+{
+ g_debug ("%s enter", __FUNCTION__);
+
+ const gchar *filename = NULL;
+
+ /* Read keys only from single file provided */
+ if (g_variant_lookup (options, "filename", "&s", &filename))
+ return _load_pk_from_file (self, filename, TRUE, error);
+
+ /* Load public keys from well-known directories and files */
+ if (!_ed25519_load_pk (self, options, TRUE, error))
+ return FALSE;
+
+ /* Load untrusted keys from well-known directories and files
+ * Ignore the failure from this function -- it is expected to have
+ * empty list of revoked keys.
+ * */
+ if (!_ed25519_load_pk (self, options, FALSE, error))
+ g_clear_error(error);
+
+ return TRUE;
+}