From 84c8164610ee3df9bbd06f0be9e37a873708ec2d Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sat, 15 Jun 2019 20:10:47 +0300 Subject: [PATCH 01/75] Add libsodium dependency Allow to configure with libsodium flag. Signed-off-by: Denis Pynkin --- Makefile-ostree.am | 4 ++++ configure.ac | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Makefile-ostree.am b/Makefile-ostree.am index f861afe4..470d23d3 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -112,6 +112,10 @@ ostree_SOURCES += \ $(NULL) endif +if USE_LIBSODIUM +ostree_CFLAGS += $(OT_DEP_LIBSODIUM_CFLAGS) +ostree_LDADD += $(OT_DEP_LIBSODIUM_LIBS) +endif # USE_LIBSODIUM if USE_CURL_OR_SOUP ostree_SOURCES += src/ostree/ot-remote-builtin-add-cookie.c \ diff --git a/configure.ac b/configure.ac index e2d867e7..e41ccb70 100644 --- a/configure.ac +++ b/configure.ac @@ -242,6 +242,21 @@ dnl to link to it directly. ) AM_CONDITIONAL(USE_GPGME, test "x$have_gpgme" = xyes) + +LIBSODIUM_DEPENDENCY="1.0.14" +AC_ARG_WITH(libsodium, + AS_HELP_STRING([--with-libsodium], [Use libsodium @<:@default=no@:>@]), + [], [with_libsodium=no]) +AS_IF([test x$with_libsodium != xno], [ + AC_DEFINE([HAVE_LIBSODIUM], 1, [Define if using libsodium]) + PKG_CHECK_MODULES(OT_DEP_LIBSODIUM, libsodium >= $LIBSODIUM_DEPENDENCY, have_libsodium=yes, have_libsodium=no) + AS_IF([ test x$have_libsodium = xno ], [ + AC_MSG_ERROR([Need LIBSODIUM version $LIBSODIUM_DEPENDENCY or later]) + ]) + OSTREE_FEATURES="$OSTREE_FEATURES libsodium" +], with_libsodium=no ) +AM_CONDITIONAL(USE_LIBSODIUM, test "x$have_libsodium" = xyes) + LIBARCHIVE_DEPENDENCY="libarchive >= 2.8.0" # What's in RHEL7.2. FUSE_DEPENDENCY="fuse >= 2.9.2" From edbbe1c4f2267c0d95d4ee14f6cbc516a1d31dca Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 29 Jul 2019 02:32:28 +0300 Subject: [PATCH 02/75] lib/sign: initial implementation Added the initial version of signing interface allowing to allowing to sign and verify commits. Implemented initial signing modules: - dummy -- simple module allowing to sign/verify with ASCII string - ed25519 -- module allowing to sign/verify commit with ed25519 (EdDSA) signature scheme provided by libsodium library. Signed-off-by: Denis Pynkin --- Makefile-libostree-defines.am | 2 + Makefile-libostree.am | 14 ++ Makefile-ostree.am | 10 +- apidoc/ostree-sections.txt | 23 ++ src/libostree/libostree-devel.sym | 16 ++ src/libostree/ostree-sign-dummy.c | 181 +++++++++++++++ src/libostree/ostree-sign-dummy.h | 63 +++++ src/libostree/ostree-sign-ed25519.c | 342 +++++++++++++++++++++++++++ src/libostree/ostree-sign-ed25519.h | 75 ++++++ src/libostree/ostree-sign.c | 347 ++++++++++++++++++++++++++++ src/libostree/ostree-sign.h | 156 +++++++++++++ 11 files changed, 1224 insertions(+), 5 deletions(-) create mode 100644 src/libostree/ostree-sign-dummy.c create mode 100644 src/libostree/ostree-sign-dummy.h create mode 100644 src/libostree/ostree-sign-ed25519.c create mode 100644 src/libostree/ostree-sign-ed25519.h create mode 100644 src/libostree/ostree-sign.c create mode 100644 src/libostree/ostree-sign.h diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am index 06035157..43e09281 100644 --- a/Makefile-libostree-defines.am +++ b/Makefile-libostree-defines.am @@ -46,6 +46,8 @@ libostree_public_headers = \ src/libostree/ostree-repo-finder-mount.h \ src/libostree/ostree-repo-finder-override.h \ src/libostree/ostree-kernel-args.h \ + src/libostree/ostree-sign.h \ + src/libostree/ostree-sign-ed25519.h \ $(NULL) # This one is generated via configure.ac, and the gtk-doc diff --git a/Makefile-libostree.am b/Makefile-libostree.am index a7e7e123..c0a7ac9f 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -262,6 +262,20 @@ libostree_1_la_CFLAGS += $(OT_DEP_SELINUX_CFLAGS) libostree_1_la_LIBADD += $(OT_DEP_SELINUX_LIBS) endif +libostree_1_la_SOURCES += \ + src/libostree/ostree-sign.c \ + src/libostree/ostree-sign.h \ + src/libostree/ostree-sign-dummy.c \ + src/libostree/ostree-sign-dummy.h \ + src/libostree/ostree-sign-ed25519.c \ + src/libostree/ostree-sign-ed25519.h \ + $(NULL) + +if USE_LIBSODIUM +libostree_1_la_CFLAGS += $(OT_DEP_LIBSODIUM_CFLAGS) +libostree_1_la_LIBADD += $(OT_DEP_LIBSODIUM_LIBS) +endif # USE_LIBSODIUM + # XXX: work around clang being passed -fstack-clash-protection which it doesn't understand # See: https://bugzilla.redhat.com/show_bug.cgi?id=1672012 INTROSPECTION_SCANNER_ENV = CC=gcc diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 470d23d3..f37d974a 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -112,11 +112,6 @@ ostree_SOURCES += \ $(NULL) endif -if USE_LIBSODIUM -ostree_CFLAGS += $(OT_DEP_LIBSODIUM_CFLAGS) -ostree_LDADD += $(OT_DEP_LIBSODIUM_LIBS) -endif # USE_LIBSODIUM - if USE_CURL_OR_SOUP ostree_SOURCES += src/ostree/ot-remote-builtin-add-cookie.c \ src/ostree/ot-remote-builtin-delete-cookie.c \ @@ -166,3 +161,8 @@ if USE_LIBARCHIVE ostree_CFLAGS += $(OT_DEP_LIBARCHIVE_CFLAGS) ostree_LDADD += $(OT_DEP_LIBARCHIVE_LIBS) endif + +if USE_LIBSODIUM +ostree_CFLAGS += $(OT_DEP_LIBSODIUM_CFLAGS) +ostree_LDADD += $(OT_DEP_LIBSODIUM_LIBS) +endif # USE_LIBSODIUM diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 3525d9f2..cfc4a340 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -705,3 +705,26 @@ ostree_kernel_args_from_string ostree_kernel_args_to_strv ostree_kernel_args_to_string + +
+ostree-sign +OstreeSign +OstreeSignDummy +OstreeSignEd25519 +ostree_sign_list_names +ostree_sign_commit +ostree_sign_commit_verify +ostree_sign_data +ostree_sign_get_by_name +ostree_sign_get_name +ostree_sign_detached_metadata_append +ostree_sign_metadata_verify +ostree_sign_load_pk +ostree_sign_set_pk +ostree_sign_set_sk +ostree_sign_ed25519_keypair_generate + +ostree_sign_get_type +ostree_sign_dummy_get_type +ostree_sign_ed25519_get_type +
diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 3d5fd3bc..4066f383 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -21,6 +21,22 @@ LIBOSTREE_2020.2 { global: ostree_repo_commit_modifier_set_sepolicy_from_commit; + someostree_symbol_deleteme; + ostree_sign_get_type; + ostree_sign_list_names; + ostree_sign_commit; + ostree_sign_commit_verify; + ostree_sign_data; + ostree_sign_get_by_name; + ostree_sign_get_name; + ostree_sign_detached_metadata_append; + ostree_sign_metadata_verify; + ostree_sign_load_pk; + ostree_sign_set_pk; + ostree_sign_set_sk; + ostree_sign_dummy_get_type; + ostree_sign_ed25519_get_type; + ostree_sign_ed25519_keypair_generate; } LIBOSTREE_2020.1; /* Stub section for the stable release *after* this development one; don't diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c new file mode 100644 index 00000000..e489a988 --- /dev/null +++ b/src/libostree/ostree-sign-dummy.c @@ -0,0 +1,181 @@ +/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */ + +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include "ostree-sign-dummy.h" +#include + +#define OSTREE_SIGN_DUMMY_NAME "dummy" + +#define OSTREE_SIGN_METADATA_DUMMY_KEY "ostree.sign.dummy" +#define OSTREE_SIGN_METADATA_DUMMY_TYPE "aay" + +#define OSTREE_SIGN_DUMMY_SIGNATURE "dummysign" + +struct _OstreeSignDummy +{ + GObject parent; + gchar *signature_ascii; +}; + +static void +ostree_sign_dummy_iface_init (OstreeSignInterface *self); + +G_DEFINE_TYPE_WITH_CODE (OstreeSignDummy, ostree_sign_dummy, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (OSTREE_TYPE_SIGN, ostree_sign_dummy_iface_init)); + +static void +ostree_sign_dummy_iface_init (OstreeSignInterface *self) +{ + g_debug ("%s enter", __FUNCTION__); + + self->data = ostree_sign_dummy_data; + self->get_name = ostree_sign_dummy_get_name; + self->metadata_key = ostree_sign_dummy_metadata_key; + self->metadata_format = ostree_sign_dummy_metadata_format; + self->metadata_verify = ostree_sign_dummy_metadata_verify; + self->set_sk = ostree_sign_dummy_set_signature; + self->set_pk = ostree_sign_dummy_set_signature; +} + +static void +ostree_sign_dummy_class_init (OstreeSignDummyClass *self) +{ + g_debug ("%s enter", __FUNCTION__); + GObjectClass *object_class = G_OBJECT_CLASS(self); +} + +static void +ostree_sign_dummy_init (OstreeSignDummy *self) +{ + g_debug ("%s enter", __FUNCTION__); + + self->signature_ascii = g_strdup(OSTREE_SIGN_DUMMY_SIGNATURE); +} + +gboolean ostree_sign_dummy_set_signature (OstreeSign *self, GVariant *key, GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + + if (sign->signature_ascii != NULL) + g_free(sign->signature_ascii); + + sign->signature_ascii = g_variant_dup_string (key, 0); + + return TRUE; +} + +gboolean ostree_sign_dummy_data (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error) +{ + + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + + OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + + *signature = g_bytes_new (sign->signature_ascii, strlen(sign->signature_ascii)); + + return TRUE; +} + +gchar * ostree_sign_dummy_get_name (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + + g_autofree gchar *name = g_strdup(OSTREE_SIGN_DUMMY_NAME); + + return g_steal_pointer (&name); +} + +gchar * ostree_sign_dummy_metadata_key (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + g_autofree gchar *key = g_strdup(OSTREE_SIGN_METADATA_DUMMY_KEY); + return g_steal_pointer (&key); +} + +gchar * ostree_sign_dummy_metadata_format (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + g_autofree gchar *type = g_strdup(OSTREE_SIGN_METADATA_DUMMY_TYPE); + return g_steal_pointer (&type); +} + +gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + + gboolean ret = FALSE; + + if (signatures == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + "signature: dummy: commit have no signatures of my type"); + goto err; + } + + + if (!g_variant_is_of_type (signatures, (GVariantType *) OSTREE_SIGN_METADATA_DUMMY_TYPE)) + { + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + "signature: dummy: wrong type passed for verification"); + goto err; + } + + for (gsize i = 0; i < g_variant_n_children(signatures); i++) + { + g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i); + g_autoptr (GBytes) signature = g_variant_get_data_as_bytes(child); + + gsize sign_size = 0; + g_bytes_get_data (signature, &sign_size); + g_autofree gchar *sign_ascii = g_strndup(g_bytes_get_data (signature, NULL), sign_size); + g_debug("Read signature %d: %s", (gint)i, sign_ascii); + + if (!g_strcmp0(sign_ascii, sign->signature_ascii)) + ret = TRUE; + } + +err: + return ret; +} diff --git a/src/libostree/ostree-sign-dummy.h b/src/libostree/ostree-sign-dummy.h new file mode 100644 index 00000000..8bbd407d --- /dev/null +++ b/src/libostree/ostree-sign-dummy.h @@ -0,0 +1,63 @@ +/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */ + +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * - Denis Pynkin (d4s) + */ + +#pragma once + +#include "ostree-sign.h" + +G_BEGIN_DECLS + +#define OSTREE_TYPE_SIGN_DUMMY (ostree_sign_dummy_get_type ()) + +_OSTREE_PUBLIC +G_DECLARE_FINAL_TYPE (OstreeSignDummy, + ostree_sign_dummy, + OSTREE, + SIGN_DUMMY, + GObject) + +gchar * ostree_sign_dummy_get_name (OstreeSign *self); + +gboolean ostree_sign_dummy_data (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error); + +gchar * ostree_sign_dummy_metadata_key (OstreeSign *self); +gchar * ostree_sign_dummy_metadata_format (OstreeSign *self); + +gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error); + +gboolean ostree_sign_dummy_set_signature (OstreeSign *self, GVariant *key, GError **error); + +void ostree_sign_dummy_finalize (GObject *gobject); + +G_END_DECLS + diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c new file mode 100644 index 00000000..e3ab6b57 --- /dev/null +++ b/src/libostree/ostree-sign-ed25519.c @@ -0,0 +1,342 @@ +/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * - Denis Pynkin (d4s) + */ + +#include "config.h" + +#include "ostree-sign-ed25519.h" +#ifdef HAVE_LIBSODIUM +#include +#endif + +#define OSTREE_SIGN_ED25519_NAME "ed25519" + +#define OSTREE_SIGN_METADATA_ED25519_KEY "ostree.sign.ed25519" +#define OSTREE_SIGN_METADATA_ED25519_TYPE "aay" + +struct _OstreeSignEd25519 +{ + GObject parent; + gboolean initialized; + guchar *secret_key; + guchar *public_key; +}; + +static void +ostree_sign_ed25519_iface_init (OstreeSignInterface *self); + +G_DEFINE_TYPE_WITH_CODE (OstreeSignEd25519, ostree_sign_ed25519, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (OSTREE_TYPE_SIGN, ostree_sign_ed25519_iface_init)); + +static void +ostree_sign_ed25519_iface_init (OstreeSignInterface *self) +{ + g_debug ("%s enter", __FUNCTION__); + + self->data = ostree_sign_ed25519_data; + self->get_name = ostree_sign_ed25519_get_name; + self->metadata_key = ostree_sign_ed25519_metadata_key; + self->metadata_format = ostree_sign_ed25519_metadata_format; + self->metadata_verify = ostree_sign_ed25519_metadata_verify; + self->set_sk = ostree_sign_ed25519_set_sk; + self->set_pk = ostree_sign_ed25519_set_pk; +} + +static void +ostree_sign_ed25519_class_init (OstreeSignEd25519Class *self) +{ + g_debug ("%s enter", __FUNCTION__); + GObjectClass *object_class = G_OBJECT_CLASS(self); +} + +static void +ostree_sign_ed25519_init (OstreeSignEd25519 *self) +{ + g_debug ("%s enter", __FUNCTION__); + + self->initialized = TRUE; + self->secret_key = NULL; + self->public_key = NULL; + +#ifdef HAVE_LIBSODIUM + if (sodium_init() < 0) + { + self->initialized = FALSE; + g_warning ("libsodium library couldn't be initialized"); + } +#else + g_error ("ed25519 signature isn't supported"); +#endif /* HAVE_LIBSODIUM */ +} + +gboolean ostree_sign_ed25519_data (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + 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)); + +#ifdef HAVE_LIBSODIUM + g_autofree guchar *sig = NULL; +#endif + + if ((sign->initialized != TRUE) || (sign->secret_key == NULL)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not able to sign: libsodium library isn't initialized properly"); + goto err; + } +#ifdef HAVE_LIBSODIUM + unsigned long long sig_size = 0; + + sig = g_malloc0(crypto_sign_BYTES); + + if (crypto_sign_detached (sig, + &sig_size, + g_bytes_get_data (data, NULL), + g_bytes_get_size (data), + sign->secret_key)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not able to sign the object"); + goto err; + } + + g_debug ("sign: data hash = 0x%x", g_bytes_hash(data)); + *signature = g_bytes_new (sig, sig_size); + return TRUE; +#endif /* HAVE_LIBSODIUM */ +err: + return FALSE; +} + +gchar * ostree_sign_ed25519_get_name (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + + g_autofree gchar *name = g_strdup (OSTREE_SIGN_ED25519_NAME); + + return g_steal_pointer (&name); +} + +gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + g_autofree gchar *key = g_strdup(OSTREE_SIGN_METADATA_ED25519_KEY); + return g_steal_pointer (&key); +} + +gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + g_autofree gchar *type = g_strdup (OSTREE_SIGN_METADATA_ED25519_TYPE); + return g_steal_pointer (&type); +} + +gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + gboolean ret = FALSE; + + OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + + if (signatures == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + "signature: ed25519: commit have no signatures of my type"); + goto err; + } + + if (!g_variant_is_of_type (signatures, (GVariantType *) OSTREE_SIGN_METADATA_ED25519_TYPE)) + { + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + "signature: ed25519: wrong type passed for verification"); + goto err; + } + + if ((sign->initialized != TRUE) || (sign->public_key == NULL)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not able to verify: libsodium library isn't initialized properly"); + goto err; + } + +#ifdef HAVE_LIBSODIUM + g_debug ("verify: data hash = 0x%x", g_bytes_hash(data)); + + for (gsize i = 0; i < g_variant_n_children(signatures); i++) + { + g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i); + g_autoptr (GBytes) signature = g_variant_get_data_as_bytes(child); + + g_autofree char * hex = g_malloc0 (crypto_sign_PUBLICKEYBYTES*2 + 1); + + g_debug("Read signature %d: %s", (gint)i, g_variant_print(child, TRUE)); + + if (crypto_sign_verify_detached ((guchar *) g_variant_get_data (child), + g_bytes_get_data (data, NULL), + g_bytes_get_size (data), + sign->public_key) != 0) + { + /* Incorrect signature! */ + g_debug("Signature couldn't be verified with key '%s'", + sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, crypto_sign_PUBLICKEYBYTES)); + } + else + { + ret = TRUE; + g_debug ("Signature verified successfully with key '%s'", + sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, crypto_sign_PUBLICKEYBYTES)); + } + } + + if (ret != TRUE) + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not able to verify: no valid signatures found"); +#endif /* HAVE_LIBSODIUM */ + + return ret; +err: + return FALSE; +} + +gboolean +ostree_sign_ed25519_keypair_generate (OstreeSign *self, + GVariant **out_secret_key, + GVariant **out_public_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)); + + if (sign->initialized != TRUE) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not able to sign -- libsodium library isn't initialized properly"); + goto err; + } + +#ifdef HAVE_LIBSODIUM + unsigned char pk[crypto_sign_PUBLICKEYBYTES]; + unsigned char sk[crypto_sign_SECRETKEYBYTES]; + + if (crypto_sign_keypair(pk, sk)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not able to generate keypair"); + goto err; + } + + *out_secret_key = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, sk, crypto_sign_SECRETKEYBYTES, sizeof(guchar)); + *out_public_key = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, pk, crypto_sign_PUBLICKEYBYTES, sizeof(guchar)); + + return TRUE; +#endif /* HAVE_LIBSODIUM */ + +err: + return FALSE; +} + +gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, + GVariant *secret_key, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + +#ifdef HAVE_LIBSODIUM + OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + g_autofree char * hex = NULL; + + g_free (sign->secret_key); + + gsize n_elements = 0; + sign->secret_key = (guchar *) g_variant_get_fixed_array (secret_key, &n_elements, sizeof(guchar)); + + if (n_elements != crypto_sign_SECRETKEYBYTES) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Incorrect ed25519 secret key"); + goto err; + } + + hex = g_malloc0 (crypto_sign_SECRETKEYBYTES*2 + 1); + g_debug ("Set ed25519 secret key = %s", sodium_bin2hex (hex, crypto_sign_SECRETKEYBYTES*2+1, sign->secret_key, n_elements)); + + return TRUE; + +err: +#endif /* HAVE_LIBSODIUM */ + return FALSE; +} + +gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, + GVariant *public_key, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + +#ifdef HAVE_LIBSODIUM + OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + g_autofree char * hex = NULL; + + gsize n_elements = 0; + g_free (sign->public_key); + sign->public_key = (guchar *) g_variant_get_fixed_array (public_key, &n_elements, sizeof(guchar)); + + hex = g_malloc0 (crypto_sign_PUBLICKEYBYTES*2 + 1); + g_debug ("Read ed25519 public key = %s", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, n_elements)); + + if (n_elements != crypto_sign_PUBLICKEYBYTES) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Incorrect ed25519 public key"); + goto err; + } + + g_debug ("Set ed25519 public key = %s", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, n_elements)); + + return TRUE; + +err: +#endif /* HAVE_LIBSODIUM */ + return FALSE; +} diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h new file mode 100644 index 00000000..d4a6b56d --- /dev/null +++ b/src/libostree/ostree-sign-ed25519.h @@ -0,0 +1,75 @@ +/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */ + +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * - Denis Pynkin (d4s) + */ + +#pragma once + +#include "ostree-sign.h" + +G_BEGIN_DECLS + +#define OSTREE_TYPE_SIGN_ED25519 (ostree_sign_ed25519_get_type ()) + +_OSTREE_PUBLIC +G_DECLARE_FINAL_TYPE (OstreeSignEd25519, + ostree_sign_ed25519, + OSTREE, + SIGN_ED25519, + GObject) + + +gboolean ostree_sign_ed25519_data (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error); + +gchar * ostree_sign_ed25519_get_name (OstreeSign *self); +gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self); +gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self); + +gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error); + +gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, + GVariant *secret_key, + GError **error); + +gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, + GVariant *public_key, + GError **error); + +void ostree_sign_ed25519_finalize (GObject *gobject); + +_OSTREE_PUBLIC +gboolean ostree_sign_ed25519_keypair_generate (OstreeSign *self, + GVariant **out_secret_key, + GVariant **out_public_key, + GError **error); + +G_END_DECLS + diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c new file mode 100644 index 00000000..96455f86 --- /dev/null +++ b/src/libostree/ostree-sign.c @@ -0,0 +1,347 @@ +/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */ + +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include "libglnx.h" +#include "otutil.h" + +#include "ostree-autocleanups.h" +#include "ostree-core.h" +#include "ostree-sign.h" +#include "ostree-sign-dummy.h" +#ifdef HAVE_LIBSODIUM +#include "ostree-sign-ed25519.h" +#endif + +#define G_LOG_DOMAIN "OSTreeSign" + +G_DEFINE_INTERFACE (OstreeSign, ostree_sign, G_TYPE_OBJECT) + +static void +ostree_sign_default_init (OstreeSignInterface *iface) +{ + g_debug ("OstreeSign initialization"); +} + +gchar * ostree_sign_metadata_key (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->metadata_key != NULL, NULL); + return OSTREE_SIGN_GET_IFACE (self)->metadata_key (self); +} + +gchar * ostree_sign_metadata_format (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->metadata_format != NULL, NULL); + return OSTREE_SIGN_GET_IFACE (self)->metadata_format (self); +} + +gboolean ostree_sign_set_sk (OstreeSign *self, + GVariant *secret_key, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + if (OSTREE_SIGN_GET_IFACE (self)->set_sk == NULL) + return TRUE; + + return OSTREE_SIGN_GET_IFACE (self)->set_sk (self, secret_key, error); +} + +gboolean ostree_sign_set_pk (OstreeSign *self, + GVariant *public_key, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + if (OSTREE_SIGN_GET_IFACE (self)->set_pk == NULL) + return TRUE; + + return OSTREE_SIGN_GET_IFACE (self)->set_pk (self, public_key, error); +} + +/* Load private keys for verification from anywhere. + * No need to have the same function for secret keys -- the signing SW must do it in it's own way + * */ +gboolean +ostree_sign_load_pk (OstreeSign *self, + gchar *remote_name, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->load_pk != NULL, FALSE); + + if (remote_name == NULL) + remote_name = OSTREE_SIGN_ALL_REMOTES; + + return OSTREE_SIGN_GET_IFACE (self)->load_pk (self, remote_name, error); +} + +gboolean ostree_sign_data (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error) +{ + + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->data != NULL, FALSE); + + return OSTREE_SIGN_GET_IFACE (self)->data (self, data, signature, cancellable, error); +} + +/* + * Adopted version of _ostree_detached_metadata_append_gpg_sig () + */ +GVariant * +ostree_sign_detached_metadata_append (OstreeSign *self, + GVariant *existing_metadata, + GBytes *signature_bytes) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (signature_bytes != NULL, FALSE); + + GVariantDict metadata_dict; + g_autoptr(GVariant) signature_data = NULL; + g_autoptr(GVariantBuilder) signature_builder = NULL; + + g_variant_dict_init (&metadata_dict, existing_metadata); + + g_autofree gchar *signature_key = ostree_sign_metadata_key(self); + g_autofree GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(self); + + signature_data = g_variant_dict_lookup_value (&metadata_dict, + signature_key, + (GVariantType*)signature_format); + + /* signature_data may be NULL */ + signature_builder = ot_util_variant_builder_from_variant (signature_data, signature_format); + + g_variant_builder_add (signature_builder, "@ay", ot_gvariant_new_ay_bytes (signature_bytes)); + + g_variant_dict_insert_value (&metadata_dict, + signature_key, + g_variant_builder_end (signature_builder)); + + return g_variant_dict_end (&metadata_dict); +} + + +gboolean +ostree_sign_metadata_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->metadata_verify != NULL, FALSE); + + return OSTREE_SIGN_GET_IFACE (self)->metadata_verify(self, data, signatures, error); +} + +gboolean +ostree_sign_commit_verify (OstreeSign *self, + OstreeRepo *repo, + const gchar *commit_checksum, + GCancellable *cancellable, + GError **error) + +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + + g_autoptr(GVariant) commit_variant = NULL; + /* Load the commit */ + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, + commit_checksum, &commit_variant, + error)) + return glnx_prefix_error (error, "Failed to read commit"); + + /* Load the metadata */ + g_autoptr(GVariant) metadata = NULL; + if (!ostree_repo_read_commit_detached_metadata (repo, + commit_checksum, + &metadata, + cancellable, + error)) + return glnx_prefix_error (error, "Failed to read detached metadata"); + + g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit_variant); + + /* XXX This is a hackish way to indicate to use ALL remote-specific + * keyrings in the signature verification. We want this when + * verifying a signed commit that's already been pulled. */ +/* + if (remote_name == NULL) + remote_name = OSTREE_ALL_REMOTES; +*/ + + g_autoptr(GVariant) signatures = NULL; + + g_autofree gchar *signature_key = ostree_sign_metadata_key(self); + g_autofree GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(self); + + if (metadata) + signatures = g_variant_lookup_value (metadata, + signature_key, + signature_format); + + + return ostree_sign_metadata_verify (self, + signed_data, + signatures, + error); +} + +const gchar * ostree_sign_get_name (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->get_name != NULL, FALSE); + + return OSTREE_SIGN_GET_IFACE (self)->get_name (self); +} + +OstreeSign * ostree_sign_get_by_name (const gchar *name, GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + GType types [] = { +#if defined(HAVE_LIBSODIUM) + OSTREE_TYPE_SIGN_ED25519, +#endif + OSTREE_TYPE_SIGN_DUMMY + }; + OstreeSign *ret = NULL; + + for (gint i=0; i < G_N_ELEMENTS(types); i++) + { + g_autoptr (OstreeSign) sign = g_object_new (types[i], NULL); + g_autofree gchar *sign_name = OSTREE_SIGN_GET_IFACE (sign)->get_name(sign); + + g_debug ("Found '%s' signing module", sign_name); + + if (g_strcmp0 (name, sign_name) == 0) + { + ret = g_steal_pointer (&sign); + break; + } + } + + if (ret == NULL) + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Requested signature type is not implemented"); + + return ret; +} + + +/** + * ostree_sign_commit: + * @self: Self + * @commit_checksum: SHA256 of given commit to sign + * @cancellable: A #GCancellable + * @error: a #GError + * + * Add a GPG signature to a commit. + */ +gboolean +ostree_sign_commit (OstreeSign *self, + OstreeRepo *repo, + const gchar *commit_checksum, + GCancellable *cancellable, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + g_autoptr(GBytes) commit_data = NULL; + g_autoptr(GBytes) signature = NULL; + g_autoptr(GVariant) commit_variant = NULL; + g_autoptr(GVariant) old_metadata = NULL; + g_autoptr(GVariant) new_metadata = NULL; + + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, + commit_checksum, &commit_variant, error)) + return glnx_prefix_error (error, "Failed to read commit"); + + if (!ostree_repo_read_commit_detached_metadata (repo, + commit_checksum, + &old_metadata, + cancellable, + error)) + return glnx_prefix_error (error, "Failed to read detached metadata"); + + // TODO: d4s: check if already signed? + + commit_data = g_variant_get_data_as_bytes (commit_variant); + + if (!ostree_sign_data (self, commit_data, &signature, + cancellable, error)) + return glnx_prefix_error (error, "Not able to sign the cobject"); + + new_metadata = + ostree_sign_detached_metadata_append (self, old_metadata, signature); + + if (!ostree_repo_write_commit_detached_metadata (repo, + commit_checksum, + new_metadata, + cancellable, + error)) + return FALSE; + + return TRUE; +} + +GStrv ostree_sign_list_names(void) +{ + g_debug ("%s enter", __FUNCTION__); + + GType types [] = { +#if defined(HAVE_LIBSODIUM) + OSTREE_TYPE_SIGN_ED25519, +#endif + OSTREE_TYPE_SIGN_DUMMY + }; + GStrv names = g_new0 (char *, G_N_ELEMENTS(types)+1); + gint i = 0; + + for (i=0; i < G_N_ELEMENTS(types); i++) + { + g_autoptr (OstreeSign) sign = g_object_new (types[i], NULL); + names[i] = OSTREE_SIGN_GET_IFACE (sign)->get_name(sign); + g_debug ("Found '%s' signing module", names[i]); + } + + return names; +} diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h new file mode 100644 index 00000000..f06206aa --- /dev/null +++ b/src/libostree/ostree-sign.h @@ -0,0 +1,156 @@ +/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */ + +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * - Denis Pynkin (d4s) + */ + +#pragma once + +#include +#include + +#include "ostree-ref.h" +#include "ostree-remote.h" +#include "ostree-types.h" + +/* Special remote */ +#define OSTREE_SIGN_ALL_REMOTES "__OSTREE_ALL_REMOTES__" + + +G_BEGIN_DECLS + +#define OSTREE_TYPE_SIGN (ostree_sign_get_type ()) + +_OSTREE_PUBLIC +G_DECLARE_INTERFACE (OstreeSign, ostree_sign, OSTREE, SIGN, GObject) + +struct _OstreeSignInterface +{ + GTypeInterface g_iface; + gchar *(* get_name) (OstreeSign *self); + gboolean (* data) (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error); + gchar *(* metadata_key) (OstreeSign *self); + gchar *(* metadata_format) (OstreeSign *self); + gboolean (* metadata_verify) (OstreeSign *self, + GBytes *data, + GVariant *metadata, + GError **error); + + gboolean (* set_sk) (OstreeSign *self, + GVariant *secret_key, + GError **error); + + gboolean (* set_pk) (OstreeSign *self, + GVariant *public_key, + GError **error); + + gboolean (* load_pk) (OstreeSign *self, + gchar *remote_name, + GError **error); + +}; + +_OSTREE_PUBLIC +const gchar * ostree_sign_get_name (OstreeSign *self); + +_OSTREE_PUBLIC +gboolean ostree_sign_data (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error); + + +_OSTREE_PUBLIC +gchar * ostree_sign_metadata_key (OstreeSign *self); + +_OSTREE_PUBLIC +gchar * ostree_sign_metadata_format (OstreeSign *self); + +_OSTREE_PUBLIC +GVariant * ostree_sign_detached_metadata_append (OstreeSign *self, + GVariant *existing_metadata, + GBytes *signature_bytes); + +_OSTREE_PUBLIC +gboolean ostree_sign_commit (OstreeSign *self, + OstreeRepo *repo, + const gchar *commit_checksum, + GCancellable *cancellable, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_sign_metadata_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_sign_commit_verify (OstreeSign *self, + OstreeRepo *repo, + const gchar *commit_checksum, + GCancellable *cancellable, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_sign_set_sk (OstreeSign *self, + GVariant *secret_key, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_sign_set_pk (OstreeSign *self, + GVariant *public_key, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_sign_load_pk (OstreeSign *self, + gchar *remote_name, + GError **error); + + +/** + * ostree_sign_list_names: + * + * Return the array with all available sign modules names. + * + * Returns: (transfer full): an array of strings, free when you used it + */ +_OSTREE_PUBLIC +GStrv ostree_sign_list_names(void); + +/** + * ostree_sign_get_by_name: + * + * Tries to find and return proper signing engine by it's name. + * + * Returns: (transfer full): a constant, free when you used it + */ +_OSTREE_PUBLIC +OstreeSign * ostree_sign_get_by_name (const gchar *name, GError **error); + +G_END_DECLS + From c3608aa56a4e1551240b7effe2c40bb609211b3d Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 31 Jul 2019 01:13:48 +0300 Subject: [PATCH 03/75] sign: add new builtin for signing This builtin allows to sign and verify commit with new signature mechanism. At the moment it is possible to use 'dummy' and 'ed25519' signing modules. 'dummy' module use any ASCII string from command line as a key for commit's signing or verification. Support of ed25519 signature is implemented with `libsoium` library. Secret and public key should be provided in hex presentation via command line. Based on 'gpg-sign' source. Signed-off-by: Denis Pynkin --- Makefile-ostree.am | 1 + src/ostree/main.c | 3 + src/ostree/ot-builtin-sign.c | 211 +++++++++++++++++++++++++++++++++++ src/ostree/ot-builtins.h | 1 + 4 files changed, 216 insertions(+) create mode 100644 src/ostree/ot-builtin-sign.c diff --git a/Makefile-ostree.am b/Makefile-ostree.am index f37d974a..e5767641 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -43,6 +43,7 @@ ostree_SOURCES = src/ostree/main.c \ src/ostree/ot-builtin-remote.c \ src/ostree/ot-builtin-reset.c \ src/ostree/ot-builtin-rev-parse.c \ + src/ostree/ot-builtin-sign.c \ src/ostree/ot-builtin-summary.c \ src/ostree/ot-builtin-show.c \ src/ostree/ot-builtin-static-delta.c \ diff --git a/src/ostree/main.c b/src/ostree/main.c index a523ff9a..a9f57392 100644 --- a/src/ostree/main.c +++ b/src/ostree/main.c @@ -109,6 +109,9 @@ static OstreeCommand commands[] = { { "rev-parse", OSTREE_BUILTIN_FLAG_NONE, ostree_builtin_rev_parse, "Output the target of a rev" }, + { "sign", OSTREE_BUILTIN_FLAG_NONE, + ostree_builtin_sign, + "Sign a commit" }, { "show", OSTREE_BUILTIN_FLAG_NONE, ostree_builtin_show, "Output a metadata object" }, diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c new file mode 100644 index 00000000..8edd5490 --- /dev/null +++ b/src/ostree/ot-builtin-sign.c @@ -0,0 +1,211 @@ +/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */ + +/* + * Copyright (C) 2015 Colin Walters + * Copyright (C) 2019 Denis Pynkin (d4s) + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Colin Walters + */ + +#include "config.h" + +#include "ot-main.h" +#include "ot-builtins.h" +#include "ostree.h" +#include "otutil.h" +#include "ostree-core-private.h" +#include "ostree-sign.h" +#include "ostree-sign-dummy.h" +#if defined(HAVE_LIBSODIUM) +#include "ostree-sign-ed25519.h" +#include +#endif + +static gboolean opt_delete; +static gboolean opt_verify; +static char *opt_sign_name; + +/* ATTENTION: + * Please remember to update the bash-completion script (bash/ostree) and + * man page (man/ostree-sign.xml) when changing the option list. + */ + +static GOptionEntry options[] = { + { "delete", 'd', 0, G_OPTION_ARG_NONE, &opt_delete, "Delete signatures having any of the KEY-IDs", NULL}, + { "verify", 0, 0, G_OPTION_ARG_NONE, &opt_verify, "Verify signatures", NULL}, + { "sign-type", 's', 0, G_OPTION_ARG_STRING, &opt_sign_name, "Signature type to use (defaults to 'ed25519')", "NAME"}, +#if defined(HAVE_LIBSODIUM) +#endif + { NULL } +}; + +static void +usage_error (GOptionContext *context, const char *message, GError **error) +{ + g_autofree char *help = g_option_context_get_help (context, TRUE, NULL); + g_printerr ("%s", help); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message); +} + +gboolean +ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(OstreeRepo) repo = NULL; + g_autoptr (OstreeSign) sign = NULL; + g_autofree char *resolved_commit = NULL; + const char *commit; + char **key_ids; + int n_key_ids, ii; + gboolean ret = FALSE; +#if defined(HAVE_LIBSODIUM) + g_autoptr (GVariant) ed25519_sk = NULL; + g_autoptr (GVariant) ed25519_pk = NULL; +#endif + + + context = g_option_context_new ("COMMIT KEY-ID..."); + + + if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) + goto out; + + if (argc < 2) + { + usage_error (context, "Need a COMMIT to sign or verify", error); + goto out; + } + + commit = argv[1]; + + if (!opt_verify && argc < 3) + { + usage_error (context, "Need at least one KEY-ID to sign with", error); + goto out; + } + + key_ids = argv + 2; + n_key_ids = argc - 2; + + if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) + goto out; + + /* Initialize crypto system */ + if (!opt_sign_name) + opt_sign_name = "ed25519"; + + sign = ostree_sign_get_by_name (opt_sign_name, error); + if (sign == NULL) + { + ret = FALSE; + goto out; + } + + for (ii = 0; ii < n_key_ids; ii++) + { + g_autoptr (GVariant) sk = NULL; + g_autoptr (GVariant) pk = NULL; + g_autofree guchar *key = NULL; + + if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) + { + // Just use the string as signature + sk = g_variant_new_string(key_ids[ii]); + pk = g_variant_new_string(key_ids[ii]); + } + if (opt_verify) + { +#if defined(HAVE_LIBSODIUM) + if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) + { + gsize key_len = 0; + key = g_malloc0 (crypto_sign_PUBLICKEYBYTES); + if (sodium_hex2bin (key, crypto_sign_PUBLICKEYBYTES, + key_ids[ii], strlen (key_ids[ii]), + NULL, &key_len, NULL) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid KEY '%s'", key_ids[ii]); + + goto out; + } + + pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + } +#endif + + if (!ostree_sign_set_pk (sign, pk, error)) + { + ret = FALSE; + goto out; + } + + if (ostree_sign_commit_verify (sign, + repo, + resolved_commit, + cancellable, + error)) + ret = TRUE; + } + else + { +#if defined(HAVE_LIBSODIUM) + if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) + { + gsize key_len = 0; + key = g_malloc0 (crypto_sign_SECRETKEYBYTES); + if (sodium_hex2bin (key, crypto_sign_SECRETKEYBYTES, + key_ids[ii], strlen (key_ids[ii]), + NULL, &key_len, NULL) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid KEY '%s'", key_ids[ii]); + + goto out; + } + + sk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + } +#endif + if (!ostree_sign_set_sk (sign, sk, error)) + { + ret = FALSE; + goto out; + } + + ret = ostree_sign_commit (sign, + repo, + resolved_commit, + cancellable, + error); + if (ret != TRUE) + goto out; + } + } + + // No valid signature found + if (opt_verify && (ret != TRUE)) + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + "No valid signatures found"); + +out: + return ret; +} diff --git a/src/ostree/ot-builtins.h b/src/ostree/ot-builtins.h index 12a99b45..e372d359 100644 --- a/src/ostree/ot-builtins.h +++ b/src/ostree/ot-builtins.h @@ -53,6 +53,7 @@ BUILTINPROTO(prune); BUILTINPROTO(refs); BUILTINPROTO(reset); BUILTINPROTO(fsck); +BUILTINPROTO(sign); BUILTINPROTO(show); BUILTINPROTO(static_delta); BUILTINPROTO(summary); From e133cb7b74aa00a13566616d63c223be8292fa4a Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 31 Jul 2019 04:00:19 +0300 Subject: [PATCH 04/75] sign: allow to sign commits from CLI Add signing ability to commit builtin. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-commit.c | 68 ++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index 4cca56d0..4bbde92e 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -31,6 +31,12 @@ #include "parse-datetime.h" #include "ostree-repo-private.h" #include "ostree-libarchive-private.h" +#include "ostree-sign.h" +#include "ostree-sign-dummy.h" +#if defined(HAVE_LIBSODIUM) +#include "ostree-sign-ed25519.h" +#include +#endif static char *opt_subject; static char *opt_body; @@ -62,9 +68,11 @@ static gint opt_owner_uid = -1; static gint opt_owner_gid = -1; static gboolean opt_table_output; #ifndef OSTREE_DISABLE_GPGME -static char **opt_key_ids; +static char **opt_gpg_key_ids; static char *opt_gpg_homedir; #endif +static char **opt_key_ids; +static char *opt_sign_name; static gboolean opt_generate_sizes; static gboolean opt_disable_fsync; static char *opt_timestamp; @@ -119,9 +127,11 @@ static GOptionEntry options[] = { { "consume", 0, 0, G_OPTION_ARG_NONE, &opt_consume, "Consume (delete) content after commit (for local directories)", NULL }, { "table-output", 0, 0, G_OPTION_ARG_NONE, &opt_table_output, "Output more information in a KEY: VALUE format", NULL }, #ifndef OSTREE_DISABLE_GPGME - { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the commit with", "KEY-ID"}, + { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_gpg_key_ids, "GPG Key ID to sign the commit with", "KEY-ID"}, { "gpg-homedir", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"}, #endif + { "sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "Sign the commit with", "KEY_ID"}, + { "sign-type", 0, 0, G_OPTION_ARG_STRING, &opt_sign_name, "Signature type to use (defaults to 'ed25519')", "NAME"}, { "generate-sizes", 0, 0, G_OPTION_ARG_NONE, &opt_generate_sizes, "Generate size information along with commit metadata", NULL }, { "disable-fsync", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_disable_fsync, "Do not invoke fsync()", NULL }, { "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" }, @@ -419,6 +429,7 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio OstreeRepoTransactionStats stats; struct CommitFilterData filter_data = { 0, }; g_autofree char *commit_body = NULL; + g_autoptr (OstreeSign) sign = NULL; context = g_option_context_new ("[PATH]"); @@ -832,12 +843,63 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio goto out; } -#ifndef OSTREE_DISABLE_GPGME if (opt_key_ids) { + /* Initialize crypto system */ + if (!opt_sign_name) + opt_sign_name = "ed25519"; + + sign = ostree_sign_get_by_name (opt_sign_name, error); + if (sign == NULL) + goto out; + char **iter; for (iter = opt_key_ids; iter && *iter; iter++) + { + const char *keyid = *iter; + g_autoptr (GVariant) secret_key = NULL; + + if (!g_strcmp0(ostree_sign_get_name (sign), "dummy")) + { + secret_key = g_variant_new_string (keyid); + } +#if defined(HAVE_LIBSODIUM) + else if (!g_strcmp0 (ostree_sign_get_name (sign), "ed25519")) + { + gsize key_len = 0; + key = g_malloc0 (crypto_sign_SECRETKEYBYTES); + if (sodium_hex2bin (key, crypto_sign_SECRETKEYBYTES, + keyid, strlen (keyid), + NULL, &key_len, NULL) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid KEY '%s'", keyid); + + goto out; + } + + secret_key = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + } +#endif + if (!ostree_sign_set_sk (sign, secret_key, error)) + goto out; + + if (!ostree_sign_commit (sign, + repo, + commit_checksum, + cancellable, + error)) + goto out; + } + } + +#ifndef OSTREE_DISABLE_GPGME + if (opt_gpg_key_ids) + { + char **iter; + + for (iter = opt_gpg_key_ids; iter && *iter; iter++) { const char *keyid = *iter; From c09df184544ccd1921855abd072dcf186712376f Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 2 Aug 2019 02:16:56 +0300 Subject: [PATCH 05/75] lib/sign: enable verification for pulling Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 92 ++++++++++++++++++++++++++--- src/libostree/ostree-sign-dummy.c | 1 - src/libostree/ostree-sign-ed25519.c | 1 - 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 381cce47..507bcc2e 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -54,6 +54,8 @@ #include #endif +#include "ostree-sign.h" + #define OSTREE_MESSAGE_FETCH_COMPLETE_ID SD_ID128_MAKE(75,ba,3d,eb,0a,f0,41,a9,a4,62,72,ff,85,d9,e7,3e) #define OSTREE_REPO_PULL_CONTENT_PRIORITY (OSTREE_FETCHER_DEFAULT_PRIORITY) @@ -105,6 +107,7 @@ typedef struct { gboolean gpg_verify; gboolean gpg_verify_summary; + gboolean sign_verify; gboolean require_static_deltas; gboolean disable_static_deltas; gboolean has_tombstone_commits; @@ -1500,6 +1503,38 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, } #endif /* OSTREE_DISABLE_GPGME */ + if (pull_data->sign_verify) + { + gboolean ret = FALSE; + g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); + /* list all signature types in detached metadata and check if signed by any? */ + GStrv names = ostree_sign_list_names(); + for (guint i=0; i < g_strv_length (names); i++) + { + g_autoptr (OstreeSign) sign = ostree_sign_get_by_name (names[i], error); + g_autoptr(GVariant) signatures = NULL; + g_autofree gchar *signature_key = ostree_sign_metadata_key (sign); + g_autofree GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format (sign); + + signatures = g_variant_lookup_value (detached_metadata, + signature_key, + signature_format); + + /* Set return to true if any sign fit */ + if (!signatures) + continue; + + if (ostree_sign_metadata_verify (sign, + signed_data, + signatures, + error + )) + ret = TRUE; + } + g_strfreev(names); + return ret; + } + return TRUE; } @@ -1829,6 +1864,28 @@ scan_commit_object (OtPullData *pull_data, } #endif /* OSTREE_DISABLE_GPGME */ + if (pull_data->sign_verify && + !g_hash_table_contains (pull_data->verified_commits, checksum)) + { + gboolean ret = FALSE; + /* list all signature types in detached metadata and check if signed by any? */ + GStrv names = ostree_sign_list_names(); + for (guint i=0; i < g_strv_length (names); i++) + { + g_autoptr (OstreeSign) sign = ostree_sign_get_by_name (names[i], error); + + if (ostree_sign_commit_verify (sign, + pull_data->repo, + checksum, + cancellable, + error)) + ret = TRUE; + } + g_strfreev(names); + if (ret == FALSE) + return FALSE; + } + /* If we found a legacy transaction flag, assume we have to scan. * We always do a scan of dirtree objects; see * https://github.com/ostreedev/ostree/issues/543 @@ -3576,6 +3633,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, g_autoptr(GSource) update_timeout = NULL; gboolean opt_gpg_verify_set = FALSE; gboolean opt_gpg_verify_summary_set = FALSE; + gboolean opt_sign_verify_set = FALSE; gboolean opt_collection_refs_set = FALSE; gboolean opt_n_network_retries_set = FALSE; gboolean opt_ref_keyring_map_set = FALSE; @@ -3610,6 +3668,8 @@ ostree_repo_pull_with_options (OstreeRepo *self, g_variant_lookup (options, "gpg-verify", "b", &pull_data->gpg_verify); opt_gpg_verify_summary_set = g_variant_lookup (options, "gpg-verify-summary", "b", &pull_data->gpg_verify_summary); + opt_sign_verify_set = + g_variant_lookup (options, "sign-verify", "b", &pull_data->sign_verify); (void) g_variant_lookup (options, "depth", "i", &pull_data->maxdepth); (void) g_variant_lookup (options, "disable-static-deltas", "b", &pull_data->disable_static_deltas); (void) g_variant_lookup (options, "require-static-deltas", "b", &pull_data->require_static_deltas); @@ -3759,7 +3819,10 @@ ostree_repo_pull_with_options (OstreeRepo *self, /* For compatibility with pull-local, don't gpg verify local * pulls by default. */ - if ((pull_data->gpg_verify || pull_data->gpg_verify_summary) && + if ((pull_data->gpg_verify || + pull_data->gpg_verify_summary || + pull_data->sign_verify + ) && pull_data->remote_name == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -3788,6 +3851,10 @@ ostree_repo_pull_with_options (OstreeRepo *self, goto out; #endif /* OSTREE_DISABLE_GPGME */ + /* TODO: read option for remote. */ + if (!opt_sign_verify_set) + opt_sign_verify_set = TRUE; + /* NOTE: If changing this, see the matching implementation in * ostree-sysroot-upgrader.c */ @@ -4647,23 +4714,28 @@ ostree_repo_pull_with_options (OstreeRepo *self, g_string_append_printf (msg, "libostree pull from '%s' for %u refs complete", pull_data->remote_name, g_hash_table_size (requested_refs_to_fetch)); - const char *verify_state; + const char *gpg_verify_state; #ifndef OSTREE_DISABLE_GPGME if (pull_data->gpg_verify_summary) { if (pull_data->gpg_verify) - verify_state = "summary+commit"; + gpg_verify_state = "summary+commit"; else - verify_state = "summary-only"; + gpg_verify_state = "summary-only"; } else - verify_state = (pull_data->gpg_verify ? "commit" : "disabled"); - g_string_append_printf (msg, "\nsecurity: GPG: %s ", verify_state); + gpg_verify_state = (pull_data->gpg_verify ? "commit" : "disabled"); + + g_string_append_printf (msg, "\nsecurity: GPG: %s ", gpg_verify_state); #else - verify_state = "disabled"; - g_string_append_printf (msg, "\nsecurity: %s ", verify_state); + gpg_verify_state = "disabled"; + g_string_append_printf (msg, "\nsecurity: %s ", gpg_verify_state); #endif /* OSTREE_DISABLE_GPGME */ + const char *sign_verify_state; + sign_verify_state = (pull_data->sign_verify ? "commit" : "disabled"); + g_string_append_printf (msg, "\nsecurity: SIGN: %s ", sign_verify_state); + OstreeFetcherURI *first_uri = pull_data->meta_mirrorlist->pdata[0]; g_autofree char *first_scheme = _ostree_fetcher_uri_get_scheme (first_uri); if (g_str_has_prefix (first_scheme, "http")) @@ -4698,7 +4770,8 @@ ostree_repo_pull_with_options (OstreeRepo *self, ot_journal_send ("MESSAGE=%s", msg->str, "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(OSTREE_MESSAGE_FETCH_COMPLETE_ID), "OSTREE_REMOTE=%s", pull_data->remote_name, - "OSTREE_GPG=%s", verify_state, + "OSTREE_SIGN=%s", sign_verify_state, + "OSTREE_GPG=%s", gpg_verify_state, "OSTREE_SECONDS=%u", n_seconds, "OSTREE_XFER_SIZE=%s", formatted_xferred, NULL); @@ -6023,6 +6096,7 @@ ostree_repo_pull_from_remotes_async (OstreeRepo *self, g_variant_dict_insert (&local_options_dict, "gpg-verify", "b", FALSE); #endif /* OSTREE_DISABLE_GPGME */ g_variant_dict_insert (&local_options_dict, "gpg-verify-summary", "b", FALSE); + g_variant_dict_insert (&local_options_dict, "sign-verify", "b", FALSE); g_variant_dict_insert (&local_options_dict, "inherit-transaction", "b", TRUE); if (result->remote->refspec_name != NULL) g_variant_dict_insert (&local_options_dict, "override-remote-name", "s", result->remote->refspec_name); diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index e489a988..4baf656c 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -64,7 +64,6 @@ static void ostree_sign_dummy_class_init (OstreeSignDummyClass *self) { g_debug ("%s enter", __FUNCTION__); - GObjectClass *object_class = G_OBJECT_CLASS(self); } static void diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index e3ab6b57..6a110104 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -67,7 +67,6 @@ static void ostree_sign_ed25519_class_init (OstreeSignEd25519Class *self) { g_debug ("%s enter", __FUNCTION__); - GObjectClass *object_class = G_OBJECT_CLASS(self); } static void From 9e8f0f4ca0350cd43c202fe25b99900450e53406 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 2 Aug 2019 02:20:33 +0300 Subject: [PATCH 06/75] tests: add test for commits sign/verification Add tests checking: - sign mechanism is in working state - module 'dummy' is able to sign/verify commit - module 'ed25519' is able to sign/verify commit - both modules could be used for the same commit - 'ostree sign' builtin works with commits - 'ostree commit' builtin able to sign commits Signed-off-by: Denis Pynkin --- Makefile-tests.am | 1 + tests/libtest.sh | 10 ++++ tests/test-signed-commit.sh | 106 ++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100755 tests/test-signed-commit.sh diff --git a/Makefile-tests.am b/Makefile-tests.am index 83b0f1a2..8e233466 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -137,6 +137,7 @@ _installed_or_uninstalled_test_scripts = \ tests/test-summary-collections.sh \ tests/test-pull-collections.sh \ tests/test-config.sh \ + tests/test-signed-commit.sh \ $(NULL) if USE_GPGME diff --git a/tests/libtest.sh b/tests/libtest.sh index c82bf487..58a9fd9b 100755 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -673,6 +673,16 @@ which_gpg () { echo ${gpg} } +has_libsodium () { + local ret + ${CMD_PREFIX} ostree --version > version.txt + grep -q -e '- libsodium' version.txt + ret=$? + rm -f version.txt + return ${ret} +} + + libtest_cleanup_gpg () { local gpg_homedir=${1:-${test_tmpdir}/gpghome} gpg-connect-agent --homedir "${gpg_homedir}" killagent /bye || true diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh new file mode 100755 index 00000000..08993c60 --- /dev/null +++ b/tests/test-signed-commit.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# +# Copyright (C) 2019 Collabora Ltd. +# +# SPDX-License-Identifier: LGPL-2.0+ +# +# This library 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 of the License, or (at your option) any later version. +# +# This library 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 a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +set -euo pipefail + +. $(dirname $0)/libtest.sh + +echo "1..6" + +mkdir ${test_tmpdir}/repo +ostree_repo_init repo --mode="archive" + +echo "Unsigned commit" > file.txt +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Unsigned commit' +COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" + +# Test `ostree sign` with dummy module first +DUMMYSIGN="dummysign" +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy ${COMMIT} ${DUMMYSIGN} + +# Ensure that detached metadata really contain expected string +EXPECTEDSIGN="$(echo $DUMMYSIGN | hexdump -n 9 -e '8/1 "0x%.2x, " 1/1 " 0x%.2x"')" +${CMD_PREFIX} ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.dummy | grep -q -e "${EXPECTEDSIGN}" +echo "ok Detached dummy signature added" + +# Verify vith sign mechanism +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} +echo "ok dummy signature verified" + +echo "Signed commit with dummy key: ${DUMMYSIGN}" >> file.txt +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Signed with dummy module' --sign=${DUMMYSIGN} --sign-type=dummy +COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} +echo "ok commit with dummy signing" + +# Test ostree sign with 'ed25519' module +# Generate private key in PEM format +PEMFILE="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.pem)" +openssl genpkey -algorithm ed25519 -outform PEM -out "${PEMFILE}" + +# tests below require libsodium support +if has_libsodium; then + # Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html + # Extract the private and public parts from generated key. + PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | hexdump -s 12 -e '16/1 "%.2x"')" + SEED="$(openssl pkey -outform DER -in ${PEMFILE} | hexdump -s 16 -e '16/1 "%.2x"')" + # Secret key is concantination of SEED and PUBLIC + SECRET="${SEED}${PUBLIC}" + + echo "SEED = $SEED" + echo "PUBLIC = $PUBLIC" + + echo "Signed commit with ed25519: ${SECRET}" >> file.txt + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s "Signed with ed25519 module" --sign=${SECRET} --sign-type=ed25519 + COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" + + # Ensure that detached metadata contain signature + ${CMD_PREFIX} ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.ed25519 &>/dev/null + echo "ok Detached ed25519 signature added" + + # Verify vith sign mechanism + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} + echo "ok ed25519 signature verified" + + # Check if we able to use all available modules to sign the same commit + echo "Unsigned commit for multi-sign" >> file.txt + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Unsigned commit' + COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" + # Check if we have no signatures + for mod in "dummy" "ed25519"; do + if ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.${mod}; then + echo "Unexpected signature for ${mod} found" + exit 1 + fi + done + + # Sign with all available modules + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy ${COMMIT} ${DUMMYSIGN} + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=ed25519 ${COMMIT} ${SECRET} + # and verify + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} + echo "ok multiple signing " +else + echo "ok Detached ed25519 signature # SKIP due libsodium unavailability" + echo "ok ed25519 signature verified # SKIP due libsodium unavailability" + echo "ok multiple signing # SKIP due libsodium unavailability" +fi From 2303202c86c96768abb42728b2e5b2090b9d0198 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 9 Aug 2019 22:07:57 +0300 Subject: [PATCH 07/75] sign: API changes for public keys and CLI keys format API changes: - added function `ostree_sign_add_pk()` for multiple public keys using. - `ostree_sign_set_pk()` now substitutes all previously added keys. - added function `ostree_sign_load_pk()` allowed to load keys from file. - `ostree_sign_ed25519_load_pk()` able to load the raw keys list from file. - use base64 encoded public and private ed25519 keys for CLI and keys file. Signed-off-by: Denis Pynkin --- apidoc/ostree-sections.txt | 1 + src/libostree/libostree-devel.sym | 1 + src/libostree/ostree-repo-pull.c | 17 +- src/libostree/ostree-sign-dummy.h | 2 - src/libostree/ostree-sign-ed25519.c | 238 +++++++++++++++++++++++++--- src/libostree/ostree-sign-ed25519.h | 9 +- src/libostree/ostree-sign.c | 19 ++- src/libostree/ostree-sign.h | 13 +- src/ostree/ot-builtin-commit.c | 9 +- src/ostree/ot-builtin-sign.c | 14 +- tests/test-signed-commit.sh | 8 +- 11 files changed, 278 insertions(+), 53 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index cfc4a340..806bd1a7 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -719,6 +719,7 @@ ostree_sign_get_by_name ostree_sign_get_name ostree_sign_detached_metadata_append ostree_sign_metadata_verify +ostree_sign_add_pk ostree_sign_load_pk ostree_sign_set_pk ostree_sign_set_sk diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 4066f383..8be5a3bf 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -33,6 +33,7 @@ global: ostree_sign_metadata_verify; ostree_sign_load_pk; ostree_sign_set_pk; + ostree_sign_add_pk; ostree_sign_set_sk; ostree_sign_dummy_get_type; ostree_sign_ed25519_get_type; diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 507bcc2e..781c2458 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1508,13 +1508,21 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, gboolean ret = FALSE; g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); /* list all signature types in detached metadata and check if signed by any? */ - GStrv names = ostree_sign_list_names(); + g_auto(GStrv) names = ostree_sign_list_names(); for (guint i=0; i < g_strv_length (names); i++) { - g_autoptr (OstreeSign) sign = ostree_sign_get_by_name (names[i], error); + g_autoptr (OstreeSign) sign = NULL; g_autoptr(GVariant) signatures = NULL; - g_autofree gchar *signature_key = ostree_sign_metadata_key (sign); - g_autofree GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format (sign); + g_autofree gchar *signature_key = NULL; + g_autofree GVariantType *signature_format = NULL; + + if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL) + { + g_error_free (*error); + continue; + } + signature_key = ostree_sign_metadata_key (sign); + signature_format = (GVariantType *) ostree_sign_metadata_format (sign); signatures = g_variant_lookup_value (detached_metadata, signature_key, @@ -1531,7 +1539,6 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, )) ret = TRUE; } - g_strfreev(names); return ret; } diff --git a/src/libostree/ostree-sign-dummy.h b/src/libostree/ostree-sign-dummy.h index 8bbd407d..73bad135 100644 --- a/src/libostree/ostree-sign-dummy.h +++ b/src/libostree/ostree-sign-dummy.h @@ -57,7 +57,5 @@ gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, gboolean ostree_sign_dummy_set_signature (OstreeSign *self, GVariant *key, GError **error); -void ostree_sign_dummy_finalize (GObject *gobject); - G_END_DECLS diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 6a110104..779c0b27 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -30,17 +30,25 @@ #include #endif +#define G_LOG_DOMAIN "OSTreeSign" + #define OSTREE_SIGN_ED25519_NAME "ed25519" #define OSTREE_SIGN_METADATA_ED25519_KEY "ostree.sign.ed25519" #define OSTREE_SIGN_METADATA_ED25519_TYPE "aay" +#if 0 +#define SIGNIFY_COMMENT_HEADER "untrusted comment:" +#define SIGNIFY_ID_LENGTH 8 +#define SIGNIFY_MAGIC_ED25519 "Ed" +#endif + struct _OstreeSignEd25519 { GObject parent; gboolean initialized; guchar *secret_key; - guchar *public_key; + GList *public_keys; }; static void @@ -61,12 +69,32 @@ ostree_sign_ed25519_iface_init (OstreeSignInterface *self) self->metadata_verify = ostree_sign_ed25519_metadata_verify; self->set_sk = ostree_sign_ed25519_set_sk; self->set_pk = ostree_sign_ed25519_set_pk; + self->add_pk = ostree_sign_ed25519_add_pk; + self->load_pk = ostree_sign_ed25519_load_pk; +} + +static void +ostree_sign_ed25519_finalize (GObject *object) +{ + g_debug ("%s enter", __FUNCTION__); +#if 0 + OstreeSignEd25519 *self = OSTREE_SIGN_ED25519 (object); + + if (self->public_keys != NULL) + g_list_free_full (self->public_keys, g_object_unref); + if (self->secret_key != NULL) + free(self->secret_key); +#endif + G_OBJECT_CLASS (ostree_sign_ed25519_parent_class)->finalize (object); } static void ostree_sign_ed25519_class_init (OstreeSignEd25519Class *self) { g_debug ("%s enter", __FUNCTION__); + GObjectClass *object_class = G_OBJECT_CLASS (self); + + object_class->finalize = ostree_sign_ed25519_finalize; } static void @@ -76,7 +104,7 @@ ostree_sign_ed25519_init (OstreeSignEd25519 *self) self->initialized = TRUE; self->secret_key = NULL; - self->public_key = NULL; + self->public_keys = NULL; #ifdef HAVE_LIBSODIUM if (sodium_init() < 0) @@ -188,7 +216,7 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, goto err; } - if ((sign->initialized != TRUE) || (sign->public_key == NULL)) + if ((sign->initialized != TRUE) || (sign->public_keys == NULL)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not able to verify: libsodium library isn't initialized properly"); @@ -207,20 +235,26 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, g_debug("Read signature %d: %s", (gint)i, g_variant_print(child, TRUE)); - if (crypto_sign_verify_detached ((guchar *) g_variant_get_data (child), - g_bytes_get_data (data, NULL), - g_bytes_get_size (data), - sign->public_key) != 0) + for (GList *public_key = sign->public_keys; + public_key != NULL; + public_key = public_key->next) { - /* Incorrect signature! */ - g_debug("Signature couldn't be verified with key '%s'", - sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, crypto_sign_PUBLICKEYBYTES)); - } - else - { - ret = TRUE; - g_debug ("Signature verified successfully with key '%s'", - sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, crypto_sign_PUBLICKEYBYTES)); + if (crypto_sign_verify_detached ((guchar *) g_variant_get_data (child), + g_bytes_get_data (data, NULL), + g_bytes_get_size (data), + public_key->data) != 0) + { + /* Incorrect signature! */ + g_debug("Signature couldn't be verified with key '%s'", + sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, public_key->data, crypto_sign_PUBLICKEYBYTES)); + } + else + { + ret = TRUE; + g_debug ("Signature verified successfully with key '%s'", + sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, public_key->data, crypto_sign_PUBLICKEYBYTES)); + break; + } } } @@ -297,7 +331,7 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, } hex = g_malloc0 (crypto_sign_SECRETKEYBYTES*2 + 1); - g_debug ("Set ed25519 secret key = %s", sodium_bin2hex (hex, crypto_sign_SECRETKEYBYTES*2+1, sign->secret_key, n_elements)); +// g_debug ("Set ed25519 secret key = %s", sodium_bin2hex (hex, crypto_sign_SECRETKEYBYTES*2+1, sign->secret_key, n_elements)); return TRUE; @@ -313,16 +347,35 @@ 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)); + + /* Substitute the key(s) with a new one */ + if (sign->public_keys != NULL) + { + g_list_free_full (sign->public_keys, g_object_unref); + sign->public_keys = NULL; + } + + return ostree_sign_ed25519_add_pk (self, public_key, error); +} + +gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, + GVariant *public_key, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + #ifdef HAVE_LIBSODIUM OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); g_autofree char * hex = NULL; + gpointer key = NULL; gsize n_elements = 0; - g_free (sign->public_key); - sign->public_key = (guchar *) g_variant_get_fixed_array (public_key, &n_elements, sizeof(guchar)); + key = (gpointer) g_variant_get_fixed_array (public_key, &n_elements, sizeof(guchar)); hex = g_malloc0 (crypto_sign_PUBLICKEYBYTES*2 + 1); - g_debug ("Read ed25519 public key = %s", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, n_elements)); + g_debug ("Read ed25519 public key = %s", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, key, n_elements)); if (n_elements != crypto_sign_PUBLICKEYBYTES) { @@ -331,7 +384,9 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, goto err; } - g_debug ("Set ed25519 public key = %s", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, sign->public_key, n_elements)); + key = g_memdup (key, n_elements); + if (g_list_find (sign->public_keys, key) == NULL) + sign->public_keys = g_list_prepend (sign->public_keys, key); return TRUE; @@ -339,3 +394,144 @@ err: #endif /* HAVE_LIBSODIUM */ return FALSE; } + + +static gboolean +load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, GError **error) +{ + g_return_val_if_fail (key_data_in, FALSE); +#ifdef HAVE_LIBSODIUM + gboolean ret = FALSE; + +#if 0 +/* Try to load the public key in signify format from the stream + * https://www.openbsd.org/papers/bsdcan-signify.html + * + * FIXME: Not sure if we need to support that format. + * */ + g_autofree gchar * comment = NULL; + while (TRUE) + { + gsize len = 0; + g_autofree char *line = g_data_input_stream_read_line (key_data_in, &len, NULL, error); + if (error) + goto err; + + if (line) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Signify format for ed25519 public key not found"); + goto err; + } + + if (comment == NULL) + { + /* Scan for the comment first and compare with prefix&suffix */ + if (g_str_has_prefix (line, SIGNIFY_COMMENT_HEADER) && g_str_has_suffix (line, "public key")) + /* Save comment without the prefix and blank space */ + comment = g_strdup (line + strlen(SIGNIFY_COMMENT_HEADER) + 1); + } + else + { + /* Read the key itself */ + /* base64 encoded key */ + gsize keylen = 0; + g_autofree guchar *key = g_base64_decode (line, &keylen); + + /* Malformed key */ + if (keylen != SIGNIFY_ID_LENGTH || + strncmp (line, SIGNIFY_MAGIC_ED25519, strlen(SIGNIFY_MAGIC_ED25519)) != 0) + continue; + + } + } +#endif /* 0 */ + + /* Use simple file format with just a list of base64 public keys per line */ + while (TRUE) + { + gsize len = 0; + g_autofree char *line = g_data_input_stream_read_line (key_data_in, &len, NULL, error); + g_autoptr (GVariant) pk = NULL; + + if (*error != NULL) + goto err; + + if (line == NULL) + goto out; + + /* Read the key itself */ + /* base64 encoded key */ + gsize key_len = 0; + g_autofree guchar *key = g_base64_decode (line, &key_len); + + pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + if (ostree_sign_ed25519_add_pk (self, pk, error)) + { + ret = TRUE; + g_debug ("Added public key: %s", line); + } + else + g_debug ("Invalid public key: %s", line); + } + +out: + return ret; + +err: +#endif /* HAVE_LIBSODIUM */ + return FALSE; +} + +gboolean +ostree_sign_ed25519_load_pk (OstreeSign *self, + GVariant *options, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + + g_autoptr (GFile) keyfile = NULL; + g_autoptr (GFileInputStream) key_stream_in = NULL; + g_autoptr (GDataInputStream) key_data_in = NULL; + + const gchar *remote_name = NULL; + const gchar *filename = NULL; + + /* Clear already loaded keys */ + if (sign->public_keys != NULL) + { + g_list_free_full (sign->public_keys, g_object_unref); + sign->public_keys = NULL; + } + + /* Check if the name of remote is provided */ + if (! g_variant_lookup (options, "remote", "&s", &remote_name)) + remote_name = OSTREE_SIGN_ALL_REMOTES; + + /* Read filename or use will-known if not provided */ + if (! g_variant_lookup (options, "filename", "&s", &filename)) + { + // TODO: define well-known places and load file(s) + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Please provide a filename to load"); + goto err; + } + + keyfile = g_file_new_for_path (filename); + key_stream_in = g_file_read (keyfile, NULL, error); + if (key_stream_in == NULL) + goto err; + + 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; + + return TRUE; +err: + return FALSE; +} + diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h index d4a6b56d..797ac138 100644 --- a/src/libostree/ostree-sign-ed25519.h +++ b/src/libostree/ostree-sign-ed25519.h @@ -63,7 +63,13 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, GVariant *public_key, GError **error); -void ostree_sign_ed25519_finalize (GObject *gobject); +gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, + GVariant *public_key, + GError **error); + +gboolean ostree_sign_ed25519_load_pk (OstreeSign *self, + GVariant *options, + GError **error); _OSTREE_PUBLIC gboolean ostree_sign_ed25519_keypair_generate (OstreeSign *self, @@ -71,5 +77,6 @@ gboolean ostree_sign_ed25519_keypair_generate (OstreeSign *self, GVariant **out_public_key, GError **error); + G_END_DECLS diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index 96455f86..0708395c 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -88,22 +88,31 @@ gboolean ostree_sign_set_pk (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->set_pk (self, public_key, error); } +gboolean ostree_sign_add_pk (OstreeSign *self, + GVariant *public_key, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + if (OSTREE_SIGN_GET_IFACE (self)->add_pk == NULL) + return TRUE; + + return OSTREE_SIGN_GET_IFACE (self)->add_pk (self, public_key, error); +} + /* Load private keys for verification from anywhere. * No need to have the same function for secret keys -- the signing SW must do it in it's own way * */ gboolean ostree_sign_load_pk (OstreeSign *self, - gchar *remote_name, + GVariant *options, GError **error) { g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->load_pk != NULL, FALSE); - if (remote_name == NULL) - remote_name = OSTREE_SIGN_ALL_REMOTES; - - return OSTREE_SIGN_GET_IFACE (self)->load_pk (self, remote_name, error); + return OSTREE_SIGN_GET_IFACE (self)->load_pk (self, options, error); } gboolean ostree_sign_data (OstreeSign *self, diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h index f06206aa..78e2487d 100644 --- a/src/libostree/ostree-sign.h +++ b/src/libostree/ostree-sign.h @@ -68,8 +68,12 @@ struct _OstreeSignInterface GVariant *public_key, GError **error); + gboolean (* add_pk) (OstreeSign *self, + GVariant *public_key, + GError **error); + gboolean (* load_pk) (OstreeSign *self, - gchar *remote_name, + GVariant *options, GError **error); }; @@ -126,9 +130,14 @@ gboolean ostree_sign_set_pk (OstreeSign *self, GVariant *public_key, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sign_add_pk (OstreeSign *self, + GVariant *public_key, + GError **error); + _OSTREE_PUBLIC gboolean ostree_sign_load_pk (OstreeSign *self, - gchar *remote_name, + GVariant *options, GError **error); diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index 4bbde92e..89ada19e 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -868,11 +868,10 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio else if (!g_strcmp0 (ostree_sign_get_name (sign), "ed25519")) { gsize key_len = 0; - key = g_malloc0 (crypto_sign_SECRETKEYBYTES); - if (sodium_hex2bin (key, crypto_sign_SECRETKEYBYTES, - keyid, strlen (keyid), - NULL, &key_len, NULL) != 0) - { + g_autofree guchar *key = g_base64_decode (keyid, &key_len); + + if ( key_len != crypto_sign_SECRETKEYBYTES) + { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid KEY '%s'", keyid); diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index 8edd5490..e36a50f1 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -136,10 +136,9 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) { gsize key_len = 0; - key = g_malloc0 (crypto_sign_PUBLICKEYBYTES); - if (sodium_hex2bin (key, crypto_sign_PUBLICKEYBYTES, - key_ids[ii], strlen (key_ids[ii]), - NULL, &key_len, NULL) != 0) + g_autofree guchar *key = g_base64_decode (key_ids[ii], &key_len); + + if ( key_len != crypto_sign_PUBLICKEYBYTES) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid KEY '%s'", key_ids[ii]); @@ -170,10 +169,9 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) { gsize key_len = 0; - key = g_malloc0 (crypto_sign_SECRETKEYBYTES); - if (sodium_hex2bin (key, crypto_sign_SECRETKEYBYTES, - key_ids[ii], strlen (key_ids[ii]), - NULL, &key_len, NULL) != 0) + g_autofree guchar *key = g_base64_decode (key_ids[ii], &key_len); + + if ( key_len != crypto_sign_SECRETKEYBYTES) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid KEY '%s'", key_ids[ii]); diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh index 08993c60..55945f8a 100755 --- a/tests/test-signed-commit.sh +++ b/tests/test-signed-commit.sh @@ -60,16 +60,16 @@ openssl genpkey -algorithm ed25519 -outform PEM -out "${PEMFILE}" if has_libsodium; then # Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html # Extract the private and public parts from generated key. - PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | hexdump -s 12 -e '16/1 "%.2x"')" - SEED="$(openssl pkey -outform DER -in ${PEMFILE} | hexdump -s 16 -e '16/1 "%.2x"')" + PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | tail -c 32 | base64)" + SEED="$(openssl pkey -outform DER -in ${PEMFILE} | tail -c 32 | base64)" # Secret key is concantination of SEED and PUBLIC - SECRET="${SEED}${PUBLIC}" + SECRET="$(echo ${SEED}${PUBLIC} | base64 -d | base64 -w 0)" echo "SEED = $SEED" echo "PUBLIC = $PUBLIC" echo "Signed commit with ed25519: ${SECRET}" >> file.txt - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s "Signed with ed25519 module" --sign=${SECRET} --sign-type=ed25519 + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s "Signed with ed25519 module" --sign="${SECRET}" --sign-type=ed25519 COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" # Ensure that detached metadata contain signature From 06cfcd9a8a2e11460ab58134477ae23657c3f2ea Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 19 Aug 2019 02:47:45 +0300 Subject: [PATCH 08/75] builtin/sign: allow to provide the file with public keys Added option `--keys-file` for `ostree sign`. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-sign.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index e36a50f1..5093e3c6 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -41,6 +41,7 @@ static gboolean opt_delete; static gboolean opt_verify; static char *opt_sign_name; +static char *opt_filename; /* ATTENTION: * Please remember to update the bash-completion script (bash/ostree) and @@ -52,6 +53,7 @@ static GOptionEntry options[] = { { "verify", 0, 0, G_OPTION_ARG_NONE, &opt_verify, "Verify signatures", NULL}, { "sign-type", 's', 0, G_OPTION_ARG_STRING, &opt_sign_name, "Signature type to use (defaults to 'ed25519')", "NAME"}, #if defined(HAVE_LIBSODIUM) + { "keys-file", 's', 0, G_OPTION_ARG_STRING, &opt_filename, "Read public key(s) from file", "NAME"}, #endif { NULL } }; @@ -198,6 +200,32 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } } + /* Read public signatures from file */ + if (opt_verify && opt_filename) + { + g_autoptr (GVariantBuilder) builder = NULL; + g_autoptr (GVariant) options = NULL; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); + g_variant_builder_add (builder, "{sv}", "test", g_variant_new_string (opt_filename)); + options = g_variant_builder_end (builder); + + if (!ostree_sign_load_pk (sign, options, error)) + { + ret = FALSE; + goto out; + } + if (ostree_sign_commit_verify (sign, + repo, + resolved_commit, + cancellable, + error)) + ret = TRUE; + if (ret != TRUE) + goto out; + } /* Check via file */ + // No valid signature found if (opt_verify && (ret != TRUE)) g_set_error_literal (error, From 0b55db9b2fad6d48216e130386a4d6980a336614 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 19 Aug 2019 02:49:50 +0300 Subject: [PATCH 09/75] tests/sign: check public keys load from file Test ed25519 public keys load from file and verify signed commit against that file. Signed-off-by: Denis Pynkin --- tests/test-signed-commit.sh | 116 +++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh index 55945f8a..2c547542 100755 --- a/tests/test-signed-commit.sh +++ b/tests/test-signed-commit.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..6" +echo "1..7" mkdir ${test_tmpdir}/repo ostree_repo_init repo --mode="archive" @@ -51,56 +51,88 @@ COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} echo "ok commit with dummy signing" +# tests below require libsodium support +if ! has_libsodium; then + echo "ok Detached ed25519 signature # SKIP due libsodium unavailability" + echo "ok ed25519 signature verified # SKIP due libsodium unavailability" + echo "ok multiple signing # SKIP due libsodium unavailability" + echo "ok verify ed25519 keys file # SKIP due libsodium unavailability" + exit 0 +fi + # Test ostree sign with 'ed25519' module # Generate private key in PEM format PEMFILE="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.pem)" openssl genpkey -algorithm ed25519 -outform PEM -out "${PEMFILE}" -# tests below require libsodium support -if has_libsodium; then - # Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html - # Extract the private and public parts from generated key. - PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | tail -c 32 | base64)" - SEED="$(openssl pkey -outform DER -in ${PEMFILE} | tail -c 32 | base64)" - # Secret key is concantination of SEED and PUBLIC - SECRET="$(echo ${SEED}${PUBLIC} | base64 -d | base64 -w 0)" +# Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html +# Extract the private and public parts from generated key. +PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | tail -c 32 | base64)" +SEED="$(openssl pkey -outform DER -in ${PEMFILE} | tail -c 32 | base64)" +# Secret key is concantination of SEED and PUBLIC +SECRET="$(echo ${SEED}${PUBLIC} | base64 -d | base64 -w 0)" - echo "SEED = $SEED" - echo "PUBLIC = $PUBLIC" +echo "SEED = $SEED" +echo "PUBLIC = $PUBLIC" - echo "Signed commit with ed25519: ${SECRET}" >> file.txt - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s "Signed with ed25519 module" --sign="${SECRET}" --sign-type=ed25519 - COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" +echo "Signed commit with ed25519: ${SECRET}" >> file.txt +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s "Signed with ed25519 module" --sign="${SECRET}" --sign-type=ed25519 +COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" - # Ensure that detached metadata contain signature - ${CMD_PREFIX} ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.ed25519 &>/dev/null - echo "ok Detached ed25519 signature added" +# Ensure that detached metadata contain signature +${CMD_PREFIX} ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.ed25519 &>/dev/null +echo "ok Detached ed25519 signature added" - # Verify vith sign mechanism - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} - echo "ok ed25519 signature verified" +# Verify vith sign mechanism +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} +echo "ok ed25519 signature verified" - # Check if we able to use all available modules to sign the same commit - echo "Unsigned commit for multi-sign" >> file.txt - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Unsigned commit' - COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" - # Check if we have no signatures - for mod in "dummy" "ed25519"; do - if ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.${mod}; then - echo "Unexpected signature for ${mod} found" - exit 1 - fi - done +# Check if we able to use all available modules to sign the same commit +echo "Unsigned commit for multi-sign" >> file.txt +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Unsigned commit' +COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" +# Check if we have no signatures +for mod in "dummy" "ed25519"; do + if ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=ostree.sign.${mod}; then + echo "Unexpected signature for ${mod} found" + exit 1 + fi +done - # Sign with all available modules - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy ${COMMIT} ${DUMMYSIGN} - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=ed25519 ${COMMIT} ${SECRET} - # and verify - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} - ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} - echo "ok multiple signing " -else - echo "ok Detached ed25519 signature # SKIP due libsodium unavailability" - echo "ok ed25519 signature verified # SKIP due libsodium unavailability" - echo "ok multiple signing # SKIP due libsodium unavailability" +# Sign with all available modules +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy ${COMMIT} ${DUMMYSIGN} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=ed25519 ${COMMIT} ${SECRET} +# and verify +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} +echo "ok multiple signing " + +# Prepare files with public ed25519 signatures +PUBKEYS="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.ed25519)" + +# Test if file contain no keys +if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT}; then + exit 1 fi +# Test if have a problem with file object +if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${test_tmpdir} ${COMMIT}; then + exit 1 +fi +# Test with single key in list +echo ${PUBLIC} > ${PUBKEYS} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} + +# Test the file with multiple keys without a valid public key +for((i=0;i<100;i++)); do + # Generate a list with some public signatures + openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 +done > ${PUBKEYS} +if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT}; then + exit 1 +fi + +# Add correct key into the list +echo ${PUBLIC} >> ${PUBKEYS} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} + +echo "ok verify ed25519 keys file" From 4b9232b1fe46cf84ba787336eb0aaa4418e96698 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 20 Aug 2019 00:56:27 +0300 Subject: [PATCH 10/75] builtin/sign: remove libsodium-specific code Use only common sign API without libsoduim parts. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-commit.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index 89ada19e..7d412639 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -32,11 +32,6 @@ #include "ostree-repo-private.h" #include "ostree-libarchive-private.h" #include "ostree-sign.h" -#include "ostree-sign-dummy.h" -#if defined(HAVE_LIBSODIUM) -#include "ostree-sign-ed25519.h" -#include -#endif static char *opt_subject; static char *opt_body; @@ -864,23 +859,13 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio { secret_key = g_variant_new_string (keyid); } -#if defined(HAVE_LIBSODIUM) else if (!g_strcmp0 (ostree_sign_get_name (sign), "ed25519")) { gsize key_len = 0; g_autofree guchar *key = g_base64_decode (keyid, &key_len); - if ( key_len != crypto_sign_SECRETKEYBYTES) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid KEY '%s'", keyid); - - goto out; - } - secret_key = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); } -#endif if (!ostree_sign_set_sk (sign, secret_key, error)) goto out; From 2d391266282d7e61476434cfcd4c7569fd1ec11b Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 26 Aug 2019 21:47:10 +0300 Subject: [PATCH 11/75] sign: fix unneeded objects creation Do not create objects just for supported modules list. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign.c | 81 ++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index 0708395c..5f7078f1 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -38,8 +38,31 @@ #include "ostree-sign-ed25519.h" #endif +#undef G_LOG_DOMAIN #define G_LOG_DOMAIN "OSTreeSign" +typedef struct +{ + gchar *name; + GType type; +} _sign_type; + +_sign_type sign_types[] = +{ +#if defined(HAVE_LIBSODIUM) + {"ed25519", 0}, +#endif + {"dummy", 0} +}; + +enum +{ +#if defined(HAVE_LIBSODIUM) + SIGN_ED25519, +#endif + SIGN_DUMMY +}; + G_DEFINE_INTERFACE (OstreeSign, ostree_sign, G_TYPE_OBJECT) static void @@ -110,7 +133,8 @@ ostree_sign_load_pk (OstreeSign *self, { g_debug ("%s enter", __FUNCTION__); - g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->load_pk != NULL, FALSE); + if (OSTREE_SIGN_GET_IFACE (self)->load_pk == NULL) + return FALSE; return OSTREE_SIGN_GET_IFACE (self)->load_pk (self, options, error); } @@ -208,14 +232,6 @@ ostree_sign_commit_verify (OstreeSign *self, g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit_variant); - /* XXX This is a hackish way to indicate to use ALL remote-specific - * keyrings in the signature verification. We want this when - * verifying a signed commit that's already been pulled. */ -/* - if (remote_name == NULL) - remote_name = OSTREE_ALL_REMOTES; -*/ - g_autoptr(GVariant) signatures = NULL; g_autofree gchar *signature_key = ostree_sign_metadata_key(self); @@ -246,33 +262,31 @@ OstreeSign * ostree_sign_get_by_name (const gchar *name, GError **error) { g_debug ("%s enter", __FUNCTION__); - GType types [] = { + OstreeSign *sign = NULL; + + /* Get types if not initialized yet */ #if defined(HAVE_LIBSODIUM) - OSTREE_TYPE_SIGN_ED25519, + if (sign_types[SIGN_ED25519].type == 0) + sign_types[SIGN_ED25519].type = OSTREE_TYPE_SIGN_ED25519; #endif - OSTREE_TYPE_SIGN_DUMMY - }; - OstreeSign *ret = NULL; + if (sign_types[SIGN_DUMMY].type == 0) + sign_types[SIGN_DUMMY].type = OSTREE_TYPE_SIGN_DUMMY; - for (gint i=0; i < G_N_ELEMENTS(types); i++) + for (gint i=0; i < G_N_ELEMENTS(sign_types); i++) { - g_autoptr (OstreeSign) sign = g_object_new (types[i], NULL); - g_autofree gchar *sign_name = OSTREE_SIGN_GET_IFACE (sign)->get_name(sign); - - g_debug ("Found '%s' signing module", sign_name); - - if (g_strcmp0 (name, sign_name) == 0) - { - ret = g_steal_pointer (&sign); - break; - } + if (g_strcmp0 (name, sign_types[i].name) == 0) + { + g_debug ("Found '%s' signing module", sign_types[i].name); + sign = g_object_new (sign_types[i].type, NULL); + break; + } } - if (ret == NULL) + if (sign == NULL) g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Requested signature type is not implemented"); - return ret; + return sign; } @@ -336,19 +350,12 @@ GStrv ostree_sign_list_names(void) { g_debug ("%s enter", __FUNCTION__); - GType types [] = { -#if defined(HAVE_LIBSODIUM) - OSTREE_TYPE_SIGN_ED25519, -#endif - OSTREE_TYPE_SIGN_DUMMY - }; - GStrv names = g_new0 (char *, G_N_ELEMENTS(types)+1); + GStrv names = g_new0 (char *, G_N_ELEMENTS(sign_types) + 1); gint i = 0; - for (i=0; i < G_N_ELEMENTS(types); i++) + for (i=0; i < G_N_ELEMENTS(sign_types); i++) { - g_autoptr (OstreeSign) sign = g_object_new (types[i], NULL); - names[i] = OSTREE_SIGN_GET_IFACE (sign)->get_name(sign); + names[i] = g_strdup(sign_types[i].name); g_debug ("Found '%s' signing module", names[i]); } From a8521a7c3b256c7f9fb55a93f4e038fb89bcc37b Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 26 Aug 2019 21:51:03 +0300 Subject: [PATCH 12/75] sign: fix error return for dummy module Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-dummy.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index 4baf656c..34660cec 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -27,6 +27,9 @@ #include "ostree-sign-dummy.h" #include +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "OSTreeSign" + #define OSTREE_SIGN_DUMMY_NAME "dummy" #define OSTREE_SIGN_METADATA_DUMMY_KEY "ostree.sign.dummy" @@ -174,6 +177,10 @@ gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, if (!g_strcmp0(sign_ascii, sign->signature_ascii)) ret = TRUE; } + if (ret == FALSE && *error == NULL) + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + "signature: dummy: incorrect signature"); err: return ret; From fe3a839ae7ddc420853635e2413fb94ca1815777 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 26 Aug 2019 21:55:53 +0300 Subject: [PATCH 13/75] builtin/sign: remove libsodium dependency Now do not need to compile/link builtin with external dependencies. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-sign.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index 5093e3c6..6baeb850 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -32,11 +32,6 @@ #include "otutil.h" #include "ostree-core-private.h" #include "ostree-sign.h" -#include "ostree-sign-dummy.h" -#if defined(HAVE_LIBSODIUM) -#include "ostree-sign-ed25519.h" -#include -#endif static gboolean opt_delete; static gboolean opt_verify; @@ -134,23 +129,12 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } if (opt_verify) { -#if defined(HAVE_LIBSODIUM) if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) { gsize key_len = 0; g_autofree guchar *key = g_base64_decode (key_ids[ii], &key_len); - - if ( key_len != crypto_sign_PUBLICKEYBYTES) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid KEY '%s'", key_ids[ii]); - - goto out; - } - pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); } -#endif if (!ostree_sign_set_pk (sign, pk, error)) { @@ -167,23 +151,13 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } else { -#if defined(HAVE_LIBSODIUM) if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) { gsize key_len = 0; g_autofree guchar *key = g_base64_decode (key_ids[ii], &key_len); - - if ( key_len != crypto_sign_SECRETKEYBYTES) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid KEY '%s'", key_ids[ii]); - - goto out; - } - sk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); } -#endif + if (!ostree_sign_set_sk (sign, sk, error)) { ret = FALSE; @@ -208,7 +182,6 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); - g_variant_builder_add (builder, "{sv}", "test", g_variant_new_string (opt_filename)); options = g_variant_builder_end (builder); if (!ostree_sign_load_pk (sign, options, error)) From 2831028c419ed33bf2ac3bf91d7c3b5f2b07194a Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 26 Aug 2019 21:59:34 +0300 Subject: [PATCH 14/75] sign: fixes for ed25519 for loading public keys from files Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-ed25519.c | 64 ++++++++++++++--------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 779c0b27..299c5441 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -30,6 +30,7 @@ #include #endif +#undef G_LOG_DOMAIN #define G_LOG_DOMAIN "OSTreeSign" #define OSTREE_SIGN_ED25519_NAME "ed25519" @@ -73,28 +74,10 @@ ostree_sign_ed25519_iface_init (OstreeSignInterface *self) self->load_pk = ostree_sign_ed25519_load_pk; } -static void -ostree_sign_ed25519_finalize (GObject *object) -{ - g_debug ("%s enter", __FUNCTION__); -#if 0 - OstreeSignEd25519 *self = OSTREE_SIGN_ED25519 (object); - - if (self->public_keys != NULL) - g_list_free_full (self->public_keys, g_object_unref); - if (self->secret_key != NULL) - free(self->secret_key); -#endif - G_OBJECT_CLASS (ostree_sign_ed25519_parent_class)->finalize (object); -} - static void ostree_sign_ed25519_class_init (OstreeSignEd25519Class *self) { g_debug ("%s enter", __FUNCTION__); - GObjectClass *object_class = G_OBJECT_CLASS (self); - - object_class->finalize = ostree_sign_ed25519_finalize; } static void @@ -154,7 +137,6 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, goto err; } - g_debug ("sign: data hash = 0x%x", g_bytes_hash(data)); *signature = g_bytes_new (sig, sig_size); return TRUE; #endif /* HAVE_LIBSODIUM */ @@ -189,9 +171,9 @@ gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) } gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error) + GBytes *data, + GVariant *signatures, + GError **error) { g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); @@ -216,7 +198,7 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, goto err; } - if ((sign->initialized != TRUE) || (sign->public_keys == NULL)) + if (sign->initialized != TRUE) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not able to verify: libsodium library isn't initialized properly"); @@ -224,6 +206,20 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, } #ifdef HAVE_LIBSODIUM + /* If no keys pre-loaded then, + * try to load public keys from storage(s) */ + if (sign->public_keys == NULL) + { + g_autoptr (GVariantBuilder) builder = NULL; + g_autoptr (GVariant) options = NULL; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + options = g_variant_builder_end (builder); + + if (!ostree_sign_ed25519_load_pk (self, options, error)) + goto err; + } + g_debug ("verify: data hash = 0x%x", g_bytes_hash(data)); for (gsize i = 0; i < g_variant_n_children(signatures); i++) @@ -397,7 +393,7 @@ err: static gboolean -load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, GError **error) +_load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, GError **error) { g_return_val_if_fail (key_data_in, FALSE); #ifdef HAVE_LIBSODIUM @@ -496,7 +492,6 @@ ostree_sign_ed25519_load_pk (OstreeSign *self, g_autoptr (GFileInputStream) key_stream_in = NULL; g_autoptr (GDataInputStream) key_data_in = NULL; - const gchar *remote_name = NULL; const gchar *filename = NULL; /* Clear already loaded keys */ @@ -506,16 +501,19 @@ ostree_sign_ed25519_load_pk (OstreeSign *self, sign->public_keys = NULL; } - /* Check if the name of remote is provided */ - if (! g_variant_lookup (options, "remote", "&s", &remote_name)) - remote_name = OSTREE_SIGN_ALL_REMOTES; - /* Read filename or use will-known if not provided */ if (! g_variant_lookup (options, "filename", "&s", &filename)) { - // TODO: define well-known places and load file(s) - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Please provide a filename to load"); + /* TODO: define well-known places to read */ + /* TODO: scan directories */ + filename = "/etc/ostree/trusted.ed25519"; + } + + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + { + g_debug ("Can't open file '%s' with pulic keys", filename); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "File object '%s' is not a regular file", filename); goto err; } @@ -527,7 +525,7 @@ ostree_sign_ed25519_load_pk (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)) + if (!_load_pk_from_stream (self, key_data_in, error)) goto err; return TRUE; From 3386893debd893bc2e82eaff437b5e16c22fc002 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 26 Aug 2019 22:08:10 +0300 Subject: [PATCH 15/75] sign: check signatures for pulled commits If `verification-key` is set for remote it is used as a public key for checking the commit pulled from that remote. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 114 ++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 11 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 781c2458..b16b668d 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1505,6 +1505,17 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, if (pull_data->sign_verify) { + /* Nothing to check if detached metadata is absent */ + if (detached_metadata == NULL) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Can't verify commit without detached metadata"); + return FALSE; + } + /* Shouldn't happen, but see comment in process_verify_result() */ + if (g_hash_table_contains (pull_data->verified_commits, checksum)) + return TRUE; + gboolean ret = FALSE; g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); /* list all signature types in detached metadata and check if signed by any? */ @@ -1512,13 +1523,14 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, for (guint i=0; i < g_strv_length (names); i++) { g_autoptr (OstreeSign) sign = NULL; - g_autoptr(GVariant) signatures = NULL; + g_autoptr (GVariant) signatures = NULL; g_autofree gchar *signature_key = NULL; g_autofree GVariantType *signature_format = NULL; + g_autofree gchar *pk_ascii = NULL; if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL) { - g_error_free (*error); + g_clear_error (error); continue; } signature_key = ostree_sign_metadata_key (sign); @@ -1528,17 +1540,55 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, signature_key, signature_format); - /* Set return to true if any sign fit */ if (!signatures) continue; + /* TODO: load keys for remote here */ + ostree_repo_get_remote_option (pull_data->repo, + pull_data->remote_name, + "verification-key", NULL, + &pk_ascii, NULL); + if (pk_ascii != NULL) + { + g_autoptr (GVariant) pk = NULL; + + if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) + { + // Just use the string as signature + pk = g_variant_new_string(pk_ascii); + } + else if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) + { + gsize key_len = 0; + g_autofree guchar *key = g_base64_decode (pk_ascii, &key_len); + pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + } + + if (!ostree_sign_set_pk (sign, pk, error)) + g_clear_error (error); + } + + /* Set return to true if any sign fit */ if (ostree_sign_metadata_verify (sign, signed_data, signatures, error )) ret = TRUE; + else + g_clear_error (error); } + + /* Mark the commit as verified to avoid double verification + * see process_verify_result () for rationale */ + if (ret) + { + g_hash_table_add (pull_data->verified_commits, g_strdup (checksum)); + } + else + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Can't verify commit"); + return ret; } @@ -1876,21 +1926,60 @@ scan_commit_object (OtPullData *pull_data, { gboolean ret = FALSE; /* list all signature types in detached metadata and check if signed by any? */ - GStrv names = ostree_sign_list_names(); + g_auto (GStrv) names = ostree_sign_list_names(); for (guint i=0; i < g_strv_length (names); i++) { - g_autoptr (OstreeSign) sign = ostree_sign_get_by_name (names[i], error); + g_autoptr (OstreeSign) sign = NULL; + g_autofree gchar *pk_ascii = NULL; + if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL) + { + g_clear_error (error); + continue; + } + /* TODO: load keys for remote here */ + ostree_repo_get_remote_option (pull_data->repo, + pull_data->remote_name, + "verification-key", NULL, + &pk_ascii, NULL); + if (pk_ascii != NULL) + { + g_autoptr (GVariant) pk = NULL; + + if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) + { + // Just use the string as signature + pk = g_variant_new_string(pk_ascii); + } + else if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) + { + gsize key_len = 0; + g_autofree guchar *key = g_base64_decode (pk_ascii, &key_len); + pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + } + + if (!ostree_sign_set_pk (sign, pk, error)) + g_clear_error (error); + } + + + /* Set return to true if any sign fit */ if (ostree_sign_commit_verify (sign, pull_data->repo, checksum, cancellable, error)) ret = TRUE; + else + g_clear_error (error); + + } + if (!ret) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Can't verify commit"); + return FALSE; } - g_strfreev(names); - if (ret == FALSE) - return FALSE; } /* If we found a legacy transaction flag, assume we have to scan. @@ -3857,10 +3946,13 @@ ostree_repo_pull_with_options (OstreeRepo *self, &pull_data->gpg_verify_summary, error)) goto out; #endif /* OSTREE_DISABLE_GPGME */ - - /* TODO: read option for remote. */ + /* Fetch verification settings from remote if it wasn't already + * explicitly set in the options. */ if (!opt_sign_verify_set) - opt_sign_verify_set = TRUE; + if (!ostree_repo_get_remote_boolean_option (self, pull_data->remote_name, + "sign-verify", TRUE, + &pull_data->sign_verify, error)) + goto out; /* NOTE: If changing this, see the matching implementation in * ostree-sysroot-upgrader.c From 82c773710cc843e04f461df982a42a4f24f1b0de Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 26 Aug 2019 22:11:32 +0300 Subject: [PATCH 16/75] tests/sign: add initial test for pulling Test if we pull signed commits from remote. Signed-off-by: Denis Pynkin --- Makefile-tests.am | 6 +++ tests/test-signed-pull.sh | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100755 tests/test-signed-pull.sh diff --git a/Makefile-tests.am b/Makefile-tests.am index 8e233466..505245cd 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -140,6 +140,12 @@ _installed_or_uninstalled_test_scripts = \ tests/test-signed-commit.sh \ $(NULL) +if USE_LIBSODIUM +_installed_or_uninstalled_test_scripts += \ + tests/test-signed-pull.sh \ + $(NULL) +endif + if USE_GPGME _installed_or_uninstalled_test_scripts += \ tests/test-remote-gpg-import.sh \ diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh new file mode 100755 index 00000000..2f4d4527 --- /dev/null +++ b/tests/test-signed-pull.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# +# Copyright (C) 2019 Collabora Ltd. +# +# SPDX-License-Identifier: LGPL-2.0+ +# +# This library 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 of the License, or (at your option) any later version. +# +# This library 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 a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +set -euo pipefail + +. $(dirname $0)/libtest.sh + +echo "1..4" + +setup_fake_remote_repo1 "archive" + +repo_mode="archive" + +function repo_init() { + cd ${test_tmpdir} + rm repo -rf + mkdir repo + ostree_repo_init repo --mode=${repo_mode} + ${CMD_PREFIX} ostree --repo=repo remote add origin $(cat httpd-address)/ostree/gnomerepo "$@" +} + +function test_signed_pull() { + local sign_type="$1" + cd ${test_tmpdir} + ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo commit ${COMMIT_ARGS} \ + -b main -s "A signed commit" --tree=ref=main + + ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u + # make sure gpg verification is correctly on + csum=$(${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo rev-parse main) + objpath=objects/${csum::2}/${csum:2}.commitmeta + remotesig=ostree-srv/gnomerepo/$objpath + localsig=repo/$objpath + mv $remotesig $remotesig.bak + if ${CMD_PREFIX} ostree --repo=repo --depth=0 pull origin main; then + assert_not_reached "pull with sign-verify unexpectedly succeeded?" + fi + # ok now check that we can pull correctly + mv $remotesig.bak $remotesig + ${CMD_PREFIX} ostree --repo=repo pull origin main + echo "ok pull ${sign_type} signed commit" + rm $localsig + ${CMD_PREFIX} ostree --repo=repo pull origin main + test -f $localsig + echo "ok re-pull ${sign_type} signature for stored commit" +} + +DUMMYSIGN="dummysign" +COMMIT_ARGS="--sign=${DUMMYSIGN} --sign-type=dummy" +repo_init --set=sign-verify=true +test_signed_pull "dummy" + + +# Test ostree sign with 'ed25519' module +# Generate private key in PEM format +PEMFILE="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.pem)" +openssl genpkey -algorithm ed25519 -outform PEM -out "${PEMFILE}" + +# Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html +# Extract the private and public parts from generated key. +PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | tail -c 32 | base64)" +SEED="$(openssl pkey -outform DER -in ${PEMFILE} | tail -c 32 | base64)" +# Secret key is concantination of SEED and PUBLIC +SECRET="$(echo ${SEED}${PUBLIC} | base64 -d | base64 -w 0)" + +echo "SEED = $SEED" +echo "PUBLIC = $PUBLIC" + +COMMIT_ARGS="--sign=${SECRET} --sign-type=ed25519" + +repo_init --set=sign-verify=true +${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-key "${PUBLIC}" +test_signed_pull "ed25519" + From 91cc294d05e5b3b942fc11d02cac3eec45a48e40 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 27 Aug 2019 00:28:44 +0300 Subject: [PATCH 17/75] lib/sign: disable mandatory signature check Do not check the signature check by default. Need to enable it explicitly. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index b16b668d..5b9548d7 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -3950,7 +3950,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, * explicitly set in the options. */ if (!opt_sign_verify_set) if (!ostree_repo_get_remote_boolean_option (self, pull_data->remote_name, - "sign-verify", TRUE, + "sign-verify", FALSE, &pull_data->sign_verify, error)) goto out; From 073876d9b2e63aa213ad2ae582843682cfb9e9bd Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 27 Aug 2019 00:51:20 +0300 Subject: [PATCH 18/75] lib/sign: add support of file with valid keys for remote Allow to use custom file with public keys for remote. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 42 ++++++++++++++++++++++++++++++-- tests/test-signed-pull.sh | 18 +++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 5b9548d7..c3672b78 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1527,6 +1527,7 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, g_autofree gchar *signature_key = NULL; g_autofree GVariantType *signature_format = NULL; g_autofree gchar *pk_ascii = NULL; + g_autofree gchar *pk_file = NULL; if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL) { @@ -1543,7 +1544,25 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, if (!signatures) continue; - /* TODO: load keys for remote here */ + /* Load keys for remote from file */ + ostree_repo_get_remote_option (pull_data->repo, + pull_data->remote_name, + "verification-file", NULL, + &pk_file, NULL); + if (pk_file != NULL) + { + g_autoptr (GVariantBuilder) builder = NULL; + g_autoptr (GVariant) options = NULL; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); + options = g_variant_builder_end (builder); + + if (!ostree_sign_load_pk (sign, options, error)) + g_clear_error (error); + } + + /* Override key if it is set explicitly */ ostree_repo_get_remote_option (pull_data->repo, pull_data->remote_name, "verification-key", NULL, @@ -1931,13 +1950,32 @@ scan_commit_object (OtPullData *pull_data, { g_autoptr (OstreeSign) sign = NULL; g_autofree gchar *pk_ascii = NULL; + g_autofree gchar *pk_file = NULL; if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL) { g_clear_error (error); continue; } - /* TODO: load keys for remote here */ + + /* Load keys for remote from file */ + ostree_repo_get_remote_option (pull_data->repo, + pull_data->remote_name, + "verification-file", NULL, + &pk_file, NULL); + if (pk_file != NULL) + { + g_autoptr (GVariantBuilder) builder = NULL; + g_autoptr (GVariant) options = NULL; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); + options = g_variant_builder_end (builder); + + if (!ostree_sign_load_pk (sign, options, error)) + g_clear_error (error); + } + ostree_repo_get_remote_option (pull_data->repo, pull_data->remote_name, "verification-key", NULL, diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh index 2f4d4527..dc922e81 100755 --- a/tests/test-signed-pull.sh +++ b/tests/test-signed-pull.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..4" +echo "1..7" setup_fake_remote_repo1 "archive" @@ -90,3 +90,19 @@ repo_init --set=sign-verify=true ${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-key "${PUBLIC}" test_signed_pull "ed25519" +# Prepare files with public ed25519 signatures +PUBKEYS="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.ed25519)" + +# Test the file with multiple keys without a valid public key +for((i=0;i<100;i++)); do + # Generate a list with some public signatures + openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 +done > ${PUBKEYS} +# Add correct key into the list +echo ${PUBLIC} >> ${PUBKEYS} + +repo_init --set=sign-verify=true +${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-file "${PUBKEYS}" +test_signed_pull "ed25519" + +echo "ok verify ed25519 keys file" From 94447617db1b3668a3dce77147008415e7ccde58 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 28 Aug 2019 03:16:22 +0300 Subject: [PATCH 19/75] lib/sign: read ed25519 public keys from well known places If not provided key of file name with keys for remote, then try to use system defaults: - /etc/ostree/trusted.ed25519 - /etc/ostree/trusted.ed25519.d/* - /usr/share/ostree/trusted.ed25519 - /usr/share/ostree/trusted.ed25519.d/* Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-ed25519.c | 101 +++++++++++++++++++++------- 1 file changed, 77 insertions(+), 24 deletions(-) diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 299c5441..211e5572 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -479,39 +479,20 @@ err: return FALSE; } -gboolean -ostree_sign_ed25519_load_pk (OstreeSign *self, - GVariant *options, - GError **error) +static gboolean +_load_pk_from_file (OstreeSign *self, + const gchar *filename, + GError **error) { g_debug ("%s enter", __FUNCTION__); - OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); - g_autoptr (GFile) keyfile = NULL; g_autoptr (GFileInputStream) key_stream_in = NULL; g_autoptr (GDataInputStream) key_data_in = NULL; - const gchar *filename = NULL; - - /* Clear already loaded keys */ - if (sign->public_keys != NULL) - { - g_list_free_full (sign->public_keys, g_object_unref); - sign->public_keys = NULL; - } - - /* Read filename or use will-known if not provided */ - if (! g_variant_lookup (options, "filename", "&s", &filename)) - { - /* TODO: define well-known places to read */ - /* TODO: scan directories */ - filename = "/etc/ostree/trusted.ed25519"; - } - if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { - g_debug ("Can't open file '%s' with pulic keys", filename); + g_debug ("Can't open file '%s' with public keys", filename); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "File object '%s' is not a regular file", filename); goto err; @@ -533,3 +514,75 @@ err: return FALSE; } +gboolean +ostree_sign_ed25519_load_pk (OstreeSign *self, + GVariant *options, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + gboolean ret = FALSE; + + /* Default paths there to find files with public keys */ + const gchar *default_dirs[] = + { + "/etc/ostree/trusted.ed25519.d", + DATADIR "/ostree/trusted.ed25519.d" + }; + const gchar *default_files[] = + { + "/etc/ostree/trusted.ed25519", + DATADIR "/ostree/trusted.ed25519" + }; + + OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + + const gchar *filename = NULL; + + /* Clear already loaded keys */ + if (sign->public_keys != NULL) + { + g_list_free_full (sign->public_keys, g_object_unref); + sign->public_keys = NULL; + } + + /* Read only file provided */ + if (g_variant_lookup (options, "filename", "&s", &filename)) + return _load_pk_from_file (self, filename, error); + + /* 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; + + /* 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); + if (dir == NULL) + { + g_clear_error (error); + continue; + } + 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; + } + } + + return ret; +} + From 36e4667973c4b17576758a9ef31d73932bb1d120 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 28 Aug 2019 04:21:22 +0300 Subject: [PATCH 20/75] builtin/sign: allow to sign with keys from secret file Read keys from secret file provided by `--keys-file=` option. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-sign.c | 111 +++++++++++++++++++++++++++-------- tests/test-signed-commit.sh | 17 +++++- 2 files changed, 104 insertions(+), 24 deletions(-) diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index 6baeb850..6ce32091 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -48,7 +48,7 @@ static GOptionEntry options[] = { { "verify", 0, 0, G_OPTION_ARG_NONE, &opt_verify, "Verify signatures", NULL}, { "sign-type", 's', 0, G_OPTION_ARG_STRING, &opt_sign_name, "Signature type to use (defaults to 'ed25519')", "NAME"}, #if defined(HAVE_LIBSODIUM) - { "keys-file", 's', 0, G_OPTION_ARG_STRING, &opt_filename, "Read public key(s) from file", "NAME"}, + { "keys-file", 's', 0, G_OPTION_ARG_STRING, &opt_filename, "Read key(s) from file", "NAME"}, #endif { NULL } }; @@ -92,7 +92,7 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, commit = argv[1]; - if (!opt_verify && argc < 3) + if (!opt_filename && argc < 3) { usage_error (context, "Need at least one KEY-ID to sign with", error); goto out; @@ -174,30 +174,95 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } } - /* Read public signatures from file */ - if (opt_verify && opt_filename) + /* Read signatures from file */ + if (opt_filename) { - g_autoptr (GVariantBuilder) builder = NULL; - g_autoptr (GVariant) options = NULL; - - builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); - options = g_variant_builder_end (builder); - - if (!ostree_sign_load_pk (sign, options, error)) + if (opt_verify) { - ret = FALSE; - goto out; + g_autoptr (GVariantBuilder) builder = NULL; + g_autoptr (GVariant) options = NULL; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); + options = g_variant_builder_end (builder); + + if (!ostree_sign_load_pk (sign, options, error)) + { + ret = FALSE; + goto out; + } + if (ostree_sign_commit_verify (sign, + repo, + resolved_commit, + cancellable, + error)) + ret = TRUE; + if (ret != TRUE) + goto out; + } /* Check via file */ + else + { /* Sign with keys from provided file */ + g_autoptr (GFile) keyfile = NULL; + g_autoptr (GFileInputStream) key_stream_in = NULL; + g_autoptr (GDataInputStream) key_data_in = NULL; + + if (!g_file_test (opt_filename, G_FILE_TEST_IS_REGULAR)) + { + g_warning ("Can't open file '%s' with keys", opt_filename); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "File object '%s' is not a regular file", opt_filename); + goto out; + } + + keyfile = g_file_new_for_path (opt_filename); + key_stream_in = g_file_read (keyfile, NULL, error); + if (key_stream_in == NULL) + goto out; + + key_data_in = g_data_input_stream_new (G_INPUT_STREAM(key_stream_in)); + g_assert (key_data_in != NULL); + + /* Use simple file format with just a list of base64 public keys per line */ + while (TRUE) + { + gsize len = 0; + g_autofree char *line = g_data_input_stream_read_line (key_data_in, &len, NULL, error); + g_autoptr (GVariant) sk = NULL; + + if (*error != NULL) + goto out; + + if (line == NULL) + goto out; + + + if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) + { + // Just use the string as signature + sk = g_variant_new_string(line); + } + + + if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) + { + gsize key_len = 0; + g_autofree guchar *key = g_base64_decode (line, &key_len); + sk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + } + + if (!ostree_sign_set_sk (sign, sk, error)) + continue; + + ret = ostree_sign_commit (sign, + repo, + resolved_commit, + cancellable, + error); + if (ret != TRUE) + goto out; + } } - if (ostree_sign_commit_verify (sign, - repo, - resolved_commit, - cancellable, - error)) - ret = TRUE; - if (ret != TRUE) - goto out; - } /* Check via file */ + } // No valid signature found if (opt_verify && (ret != TRUE)) diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh index 2c547542..ce29b1e4 100755 --- a/tests/test-signed-commit.sh +++ b/tests/test-signed-commit.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..7" +echo "1..8" mkdir ${test_tmpdir}/repo ostree_repo_init repo --mode="archive" @@ -57,6 +57,7 @@ if ! has_libsodium; then echo "ok ed25519 signature verified # SKIP due libsodium unavailability" echo "ok multiple signing # SKIP due libsodium unavailability" echo "ok verify ed25519 keys file # SKIP due libsodium unavailability" + echo "ok sign with ed25519 keys file # SKIP due libsodium unavailability" exit 0 fi @@ -136,3 +137,17 @@ echo ${PUBLIC} >> ${PUBKEYS} ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} echo "ok verify ed25519 keys file" + +# Check ed25519 signing with secret file +echo "Unsigned commit for secret file usage" >> file.txt +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s 'Unsigned commit' +COMMIT="$(ostree --repo=${test_tmpdir}/repo rev-parse main)" + +KEYFILE="$(mktemp -p ${test_tmpdir} secret_XXXXXX.ed25519)" +echo "${SECRET}" > ${KEYFILE} +# Sign +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=ed25519 --keys-file=${KEYFILE} ${COMMIT} +# Verify +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} +echo "ok sign with ed25519 keys file" + From 5fc2ddff30c5c8b40e60fd4dd7a6b8c1fab2e98e Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 30 Aug 2019 00:54:17 +0300 Subject: [PATCH 21/75] tests/gpg: skip test in JS if GPG is not supported Skip the single JS test which throws an error if GPG support is disabled in a build time. Signed-off-by: Denis Pynkin --- tests/test-remotes-config-dir.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/test-remotes-config-dir.js b/tests/test-remotes-config-dir.js index 5588116b..f73a82ef 100755 --- a/tests/test-remotes-config-dir.js +++ b/tests/test-remotes-config-dir.js @@ -94,16 +94,22 @@ print("ok add-in-remotes-config-dir"); // Trying to set a remote config option via write_config() for a remote // defined in the config file should succeed -let [, gpg_verify] = repo.remote_get_gpg_verify('bar'); -assertEquals(gpg_verify, true); -repoConfig = repo.copy_config(); -repoConfig.set_boolean('remote "bar"', 'gpg-verify', false); -repo.write_config(repoConfig); -repo.reload_config(null); -[, gpg_verify] = repo.remote_get_gpg_verify('bar'); -assertEquals(gpg_verify, false); - -print("ok config-remote-in-config-file-succeeds"); +try { + let [, gpg_verify] = repo.remote_get_gpg_verify('bar'); + assertEquals(gpg_verify, true); + repoConfig = repo.copy_config(); + repoConfig.set_boolean('remote "bar"', 'gpg-verify', false); + repo.write_config(repoConfig); + repo.reload_config(null); + [, gpg_verify] = repo.remote_get_gpg_verify('bar'); + assertEquals(gpg_verify, false); + print("ok config-remote-in-config-file-succeeds"); +} catch (e) { + // Skip this test if GPG is not supported + if (!(e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_SUPPORTED))) + throw e; + print("ok config-remote-in-config-file-succeeds # SKIP due build without GPG support"); +} // Trying to set a remote config option via write_config() for a remote // defined in the config dir should fail with G_IO_ERROR_EXISTS From 557f42360945b4f70196d7ec4c50443e10918573 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Thu, 5 Sep 2019 02:04:25 +0300 Subject: [PATCH 22/75] sign: fix memory leaks and code cleanup Return `const char *` instead of copy of the string -- this allow to avoid unneeded copying and memory leaks in some constructions. Minor code cleanup and optimisations. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 52 ++++++++++++----------------- src/libostree/ostree-sign-dummy.c | 16 ++++----- src/libostree/ostree-sign-dummy.h | 6 ++-- src/libostree/ostree-sign-ed25519.c | 46 +++++++++++-------------- src/libostree/ostree-sign-ed25519.h | 6 ++-- src/libostree/ostree-sign.c | 14 ++++---- src/libostree/ostree-sign.h | 10 +++--- 7 files changed, 65 insertions(+), 85 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index c3672b78..b87f6c90 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1519,28 +1519,26 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, gboolean ret = FALSE; g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); /* list all signature types in detached metadata and check if signed by any? */ - g_auto(GStrv) names = ostree_sign_list_names(); + g_auto (GStrv) names = ostree_sign_list_names(); for (guint i=0; i < g_strv_length (names); i++) { g_autoptr (OstreeSign) sign = NULL; + g_autoptr (GError) local_error = NULL; g_autoptr (GVariant) signatures = NULL; - g_autofree gchar *signature_key = NULL; - g_autofree GVariantType *signature_format = NULL; + const gchar *signature_key = NULL; + GVariantType *signature_format = NULL; g_autofree gchar *pk_ascii = NULL; g_autofree gchar *pk_file = NULL; - if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL) - { - g_clear_error (error); - continue; - } + if ((sign = ostree_sign_get_by_name (names[i], &local_error)) == NULL) + continue; + signature_key = ostree_sign_metadata_key (sign); signature_format = (GVariantType *) ostree_sign_metadata_format (sign); signatures = g_variant_lookup_value (detached_metadata, signature_key, signature_format); - if (!signatures) continue; @@ -1558,8 +1556,8 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); options = g_variant_builder_end (builder); - if (!ostree_sign_load_pk (sign, options, error)) - g_clear_error (error); + if (!ostree_sign_load_pk (sign, options, &local_error)) + g_clear_error (&local_error); } /* Override key if it is set explicitly */ @@ -1583,27 +1581,23 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); } - if (!ostree_sign_set_pk (sign, pk, error)) - g_clear_error (error); + if (!ostree_sign_set_pk (sign, pk, &local_error)) + continue; } /* Set return to true if any sign fit */ if (ostree_sign_metadata_verify (sign, signed_data, signatures, - error + &local_error )) ret = TRUE; - else - g_clear_error (error); } /* Mark the commit as verified to avoid double verification * see process_verify_result () for rationale */ if (ret) - { g_hash_table_add (pull_data->verified_commits, g_strdup (checksum)); - } else g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't verify commit"); @@ -1946,17 +1940,15 @@ scan_commit_object (OtPullData *pull_data, gboolean ret = FALSE; /* list all signature types in detached metadata and check if signed by any? */ g_auto (GStrv) names = ostree_sign_list_names(); - for (guint i=0; i < g_strv_length (names); i++) + for (char **iter=names; iter && *iter; iter++) { g_autoptr (OstreeSign) sign = NULL; + g_autoptr (GError) local_error = NULL; g_autofree gchar *pk_ascii = NULL; g_autofree gchar *pk_file = NULL; - if ((sign = ostree_sign_get_by_name (names[i], error)) == NULL) - { - g_clear_error (error); - continue; - } + if ((sign = ostree_sign_get_by_name (*iter, &local_error)) == NULL) + continue; /* Load keys for remote from file */ ostree_repo_get_remote_option (pull_data->repo, @@ -1972,8 +1964,8 @@ scan_commit_object (OtPullData *pull_data, g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); options = g_variant_builder_end (builder); - if (!ostree_sign_load_pk (sign, options, error)) - g_clear_error (error); + if (!ostree_sign_load_pk (sign, options, &local_error)) + g_clear_error (&local_error); } ostree_repo_get_remote_option (pull_data->repo, @@ -1996,8 +1988,8 @@ scan_commit_object (OtPullData *pull_data, pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); } - if (!ostree_sign_set_pk (sign, pk, error)) - g_clear_error (error); + if (!ostree_sign_set_pk (sign, pk, &local_error)) + continue; } @@ -2006,10 +1998,8 @@ scan_commit_object (OtPullData *pull_data, pull_data->repo, checksum, cancellable, - error)) + &local_error)) ret = TRUE; - else - g_clear_error (error); } if (!ret) diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index 34660cec..fb5a4f9e 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -108,30 +108,26 @@ gboolean ostree_sign_dummy_data (OstreeSign *self, return TRUE; } -gchar * ostree_sign_dummy_get_name (OstreeSign *self) +const gchar * ostree_sign_dummy_get_name (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - g_autofree gchar *name = g_strdup(OSTREE_SIGN_DUMMY_NAME); - - return g_steal_pointer (&name); + return OSTREE_SIGN_DUMMY_NAME; } -gchar * ostree_sign_dummy_metadata_key (OstreeSign *self) +const gchar * ostree_sign_dummy_metadata_key (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); - g_autofree gchar *key = g_strdup(OSTREE_SIGN_METADATA_DUMMY_KEY); - return g_steal_pointer (&key); + return OSTREE_SIGN_METADATA_DUMMY_KEY; } -gchar * ostree_sign_dummy_metadata_format (OstreeSign *self) +const gchar * ostree_sign_dummy_metadata_format (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); - g_autofree gchar *type = g_strdup(OSTREE_SIGN_METADATA_DUMMY_TYPE); - return g_steal_pointer (&type); + return OSTREE_SIGN_METADATA_DUMMY_TYPE; } gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, diff --git a/src/libostree/ostree-sign-dummy.h b/src/libostree/ostree-sign-dummy.h index 73bad135..847a7313 100644 --- a/src/libostree/ostree-sign-dummy.h +++ b/src/libostree/ostree-sign-dummy.h @@ -39,7 +39,7 @@ G_DECLARE_FINAL_TYPE (OstreeSignDummy, SIGN_DUMMY, GObject) -gchar * ostree_sign_dummy_get_name (OstreeSign *self); +const gchar * ostree_sign_dummy_get_name (OstreeSign *self); gboolean ostree_sign_dummy_data (OstreeSign *self, GBytes *data, @@ -47,8 +47,8 @@ gboolean ostree_sign_dummy_data (OstreeSign *self, GCancellable *cancellable, GError **error); -gchar * ostree_sign_dummy_metadata_key (OstreeSign *self); -gchar * ostree_sign_dummy_metadata_format (OstreeSign *self); +const gchar * ostree_sign_dummy_metadata_key (OstreeSign *self); +const gchar * ostree_sign_dummy_metadata_format (OstreeSign *self); gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, GBytes *data, diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 211e5572..c6c16302 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -112,7 +112,7 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); #ifdef HAVE_LIBSODIUM - g_autofree guchar *sig = NULL; + guchar *sig = NULL; #endif if ((sign->initialized != TRUE) || (sign->secret_key == NULL)) @@ -137,37 +137,33 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, goto err; } - *signature = g_bytes_new (sig, sig_size); + *signature = g_bytes_new_take (sig, sig_size); return TRUE; #endif /* HAVE_LIBSODIUM */ err: return FALSE; } -gchar * ostree_sign_ed25519_get_name (OstreeSign *self) +const gchar * ostree_sign_ed25519_get_name (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - g_autofree gchar *name = g_strdup (OSTREE_SIGN_ED25519_NAME); - - return g_steal_pointer (&name); + return OSTREE_SIGN_ED25519_NAME; } -gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self) +const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); - g_autofree gchar *key = g_strdup(OSTREE_SIGN_METADATA_ED25519_KEY); - return g_steal_pointer (&key); + return OSTREE_SIGN_METADATA_ED25519_KEY; } -gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) +const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); - g_autofree gchar *type = g_strdup (OSTREE_SIGN_METADATA_ED25519_TYPE); - return g_steal_pointer (&type); + return OSTREE_SIGN_METADATA_ED25519_TYPE; } gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, @@ -187,7 +183,7 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "signature: ed25519: commit have no signatures of my type"); - goto err; + goto out; } if (!g_variant_is_of_type (signatures, (GVariantType *) OSTREE_SIGN_METADATA_ED25519_TYPE)) @@ -195,14 +191,14 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "signature: ed25519: wrong type passed for verification"); - goto err; + goto out; } if (sign->initialized != TRUE) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not able to verify: libsodium library isn't initialized properly"); - goto err; + goto out; } #ifdef HAVE_LIBSODIUM @@ -217,7 +213,7 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, options = g_variant_builder_end (builder); if (!ostree_sign_ed25519_load_pk (self, options, error)) - goto err; + goto out; } g_debug ("verify: data hash = 0x%x", g_bytes_hash(data)); @@ -259,9 +255,8 @@ gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, "Not able to verify: no valid signatures found"); #endif /* HAVE_LIBSODIUM */ +out: return ret; -err: - return FALSE; } gboolean @@ -312,7 +307,6 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, #ifdef HAVE_LIBSODIUM OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); - g_autofree char * hex = NULL; g_free (sign->secret_key); @@ -326,9 +320,6 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, goto err; } - hex = g_malloc0 (crypto_sign_SECRETKEYBYTES*2 + 1); -// g_debug ("Set ed25519 secret key = %s", sodium_bin2hex (hex, crypto_sign_SECRETKEYBYTES*2+1, sign->secret_key, n_elements)); - return TRUE; err: @@ -348,7 +339,7 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, /* Substitute the key(s) with a new one */ if (sign->public_keys != NULL) { - g_list_free_full (sign->public_keys, g_object_unref); + g_list_free_full (sign->public_keys, g_free); sign->public_keys = NULL; } @@ -380,9 +371,11 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, goto err; } - key = g_memdup (key, n_elements); if (g_list_find (sign->public_keys, key) == NULL) - sign->public_keys = g_list_prepend (sign->public_keys, key); + { + gpointer newkey = g_memdup (key, n_elements); + sign->public_keys = g_list_prepend (sign->public_keys, newkey); + } return TRUE; @@ -485,6 +478,7 @@ _load_pk_from_file (OstreeSign *self, GError **error) { g_debug ("%s enter", __FUNCTION__); + g_debug ("Processing file '%s'", filename); g_autoptr (GFile) keyfile = NULL; g_autoptr (GFileInputStream) key_stream_in = NULL; @@ -542,7 +536,7 @@ ostree_sign_ed25519_load_pk (OstreeSign *self, /* Clear already loaded keys */ if (sign->public_keys != NULL) { - g_list_free_full (sign->public_keys, g_object_unref); + g_list_free_full (sign->public_keys, g_free); sign->public_keys = NULL; } diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h index 797ac138..eb8f6701 100644 --- a/src/libostree/ostree-sign-ed25519.h +++ b/src/libostree/ostree-sign-ed25519.h @@ -46,9 +46,9 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, GCancellable *cancellable, GError **error); -gchar * ostree_sign_ed25519_get_name (OstreeSign *self); -gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self); -gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self); +const gchar * ostree_sign_ed25519_get_name (OstreeSign *self); +const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self); +const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self); gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, GBytes *data, diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index 5f7078f1..6e67acaa 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -71,7 +71,7 @@ ostree_sign_default_init (OstreeSignInterface *iface) g_debug ("OstreeSign initialization"); } -gchar * ostree_sign_metadata_key (OstreeSign *self) +const gchar * ostree_sign_metadata_key (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); @@ -79,7 +79,7 @@ gchar * ostree_sign_metadata_key (OstreeSign *self) return OSTREE_SIGN_GET_IFACE (self)->metadata_key (self); } -gchar * ostree_sign_metadata_format (OstreeSign *self) +const gchar * ostree_sign_metadata_format (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); @@ -134,7 +134,7 @@ ostree_sign_load_pk (OstreeSign *self, g_debug ("%s enter", __FUNCTION__); if (OSTREE_SIGN_GET_IFACE (self)->load_pk == NULL) - return FALSE; + return TRUE; return OSTREE_SIGN_GET_IFACE (self)->load_pk (self, options, error); } @@ -170,8 +170,8 @@ ostree_sign_detached_metadata_append (OstreeSign *self, g_variant_dict_init (&metadata_dict, existing_metadata); - g_autofree gchar *signature_key = ostree_sign_metadata_key(self); - g_autofree GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(self); + const gchar *signature_key = ostree_sign_metadata_key(self); + GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(self); signature_data = g_variant_dict_lookup_value (&metadata_dict, signature_key, @@ -234,8 +234,8 @@ ostree_sign_commit_verify (OstreeSign *self, g_autoptr(GVariant) signatures = NULL; - g_autofree gchar *signature_key = ostree_sign_metadata_key(self); - g_autofree GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(self); + const gchar *signature_key = ostree_sign_metadata_key(self); + GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(self); if (metadata) signatures = g_variant_lookup_value (metadata, diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h index 78e2487d..a9648cb1 100644 --- a/src/libostree/ostree-sign.h +++ b/src/libostree/ostree-sign.h @@ -47,14 +47,14 @@ G_DECLARE_INTERFACE (OstreeSign, ostree_sign, OSTREE, SIGN, GObject) struct _OstreeSignInterface { GTypeInterface g_iface; - gchar *(* get_name) (OstreeSign *self); + const gchar *(* get_name) (OstreeSign *self); gboolean (* data) (OstreeSign *self, GBytes *data, GBytes **signature, GCancellable *cancellable, GError **error); - gchar *(* metadata_key) (OstreeSign *self); - gchar *(* metadata_format) (OstreeSign *self); + const gchar *(* metadata_key) (OstreeSign *self); + const gchar *(* metadata_format) (OstreeSign *self); gboolean (* metadata_verify) (OstreeSign *self, GBytes *data, GVariant *metadata, @@ -90,10 +90,10 @@ gboolean ostree_sign_data (OstreeSign *self, _OSTREE_PUBLIC -gchar * ostree_sign_metadata_key (OstreeSign *self); +const gchar * ostree_sign_metadata_key (OstreeSign *self); _OSTREE_PUBLIC -gchar * ostree_sign_metadata_format (OstreeSign *self); +const gchar * ostree_sign_metadata_format (OstreeSign *self); _OSTREE_PUBLIC GVariant * ostree_sign_detached_metadata_append (OstreeSign *self, From ea291a0605999242491fed982430c25aebbb2f9e Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Thu, 5 Sep 2019 16:33:52 +0300 Subject: [PATCH 23/75] builtin/sign: allow to use multiple public keys for verification `ostree sign` is able to use several public keys provided via arguments and via file with keys. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-sign.c | 62 ++++++++++++++++++++---------------- tests/test-signed-commit.sh | 18 +++++++++++ 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index 6ce32091..b1c9a73b 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -64,8 +64,8 @@ usage_error (GOptionContext *context, const char *message, GError **error) gboolean ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { - g_autoptr(GOptionContext) context = NULL; - g_autoptr(OstreeRepo) repo = NULL; + g_autoptr (GOptionContext) context = NULL; + g_autoptr (OstreeRepo) repo = NULL; g_autoptr (OstreeSign) sign = NULL; g_autofree char *resolved_commit = NULL; const char *commit; @@ -92,7 +92,10 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, commit = argv[1]; - if (!opt_filename && argc < 3) + /* Verification could be done via system files with public keys */ + if (!opt_verify && + !opt_filename && + argc < 3) { usage_error (context, "Need at least one KEY-ID to sign with", error); goto out; @@ -110,10 +113,7 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, sign = ostree_sign_get_by_name (opt_sign_name, error); if (sign == NULL) - { - ret = FALSE; - goto out; - } + goto out; for (ii = 0; ii < n_key_ids; ii++) { @@ -129,6 +129,9 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } if (opt_verify) { + g_autoptr (GError) local_error = NULL; + + if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) { gsize key_len = 0; @@ -136,17 +139,14 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); } - if (!ostree_sign_set_pk (sign, pk, error)) - { - ret = FALSE; - goto out; - } + if (!ostree_sign_set_pk (sign, pk, &local_error)) + continue; if (ostree_sign_commit_verify (sign, repo, resolved_commit, cancellable, - error)) + &local_error)) ret = TRUE; } else @@ -174,34 +174,36 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } } - /* Read signatures from file */ - if (opt_filename) + /* Try to verify with user-provided file or system configuration */ + if (opt_verify) { - if (opt_verify) + if ((n_key_ids == 0) || opt_filename) { g_autoptr (GVariantBuilder) builder = NULL; g_autoptr (GVariant) options = NULL; builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); + /* The last chance for verification source -- system files */ + if (opt_filename) + g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); options = g_variant_builder_end (builder); if (!ostree_sign_load_pk (sign, options, error)) - { - ret = FALSE; - goto out; - } + goto out; + if (ostree_sign_commit_verify (sign, repo, resolved_commit, cancellable, error)) ret = TRUE; - if (ret != TRUE) - goto out; } /* Check via file */ - else - { /* Sign with keys from provided file */ + } + else + { + /* Sign with keys from provided file */ + if (opt_filename) + { g_autoptr (GFile) keyfile = NULL; g_autoptr (GFileInputStream) key_stream_in = NULL; g_autoptr (GDataInputStream) key_data_in = NULL; @@ -233,7 +235,7 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, goto out; if (line == NULL) - goto out; + break; if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) @@ -251,7 +253,10 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } if (!ostree_sign_set_sk (sign, sk, error)) - continue; + { + ret = FALSE; + goto out; + } ret = ostree_sign_commit (sign, repo, @@ -271,5 +276,8 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, "No valid signatures found"); out: + /* It is possible to have an error due multiple signatures check */ + if (ret == TRUE) + g_clear_error (error); return ret; } diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh index ce29b1e4..e4138817 100755 --- a/tests/test-signed-commit.sh +++ b/tests/test-signed-commit.sh @@ -73,6 +73,8 @@ SEED="$(openssl pkey -outform DER -in ${PEMFILE} | tail -c 32 | base64)" # Secret key is concantination of SEED and PUBLIC SECRET="$(echo ${SEED}${PUBLIC} | base64 -d | base64 -w 0)" +WRONG_PUBLIC="$(openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64)" + echo "SEED = $SEED" echo "PUBLIC = $PUBLIC" @@ -85,7 +87,15 @@ ${CMD_PREFIX} ostree --repo=repo show ${COMMIT} --print-detached-metadata-key=os echo "ok Detached ed25519 signature added" # Verify vith sign mechanism +if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${WRONG_PUBLIC}; then + exit 1 +fi ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} ${PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${WRONG_PUBLIC} ${PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${WRONG_PUBLIC} ${WRONG_PUBLIC} ${PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} ${WRONG_PUBLIC} ${WRONG_PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${WRONG_PUBLIC} ${WRONG_PUBLIC} ${PUBLIC} ${WRONG_PUBLIC} ${WRONG_PUBLIC} echo "ok ed25519 signature verified" # Check if we able to use all available modules to sign the same commit @@ -128,9 +138,17 @@ for((i=0;i<100;i++)); do # Generate a list with some public signatures openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 done > ${PUBKEYS} +# Check if file contain no valid signatures if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT}; then exit 1 fi +# Check if no valid signatures provided via args&file +if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} ${WRONG_PUBLIC}; then + exit 1 +fi + +#Test keys file and public key +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} ${PUBLIC} # Add correct key into the list echo ${PUBLIC} >> ${PUBKEYS} From 95ab57c17ee2fcc93ee53b9a46ee6ed0a1c07b2b Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 6 Oct 2019 23:40:04 +0300 Subject: [PATCH 24/75] lib/sign-ed25519: cleanup unneeded code Removed unused code. Signed-off-by: Denis Pynkin --- apidoc/ostree-sections.txt | 1 - src/libostree/libostree-devel.sym | 1 - src/libostree/ostree-sign-ed25519.c | 89 ----------------------------- src/libostree/ostree-sign-ed25519.h | 21 +++---- 4 files changed, 7 insertions(+), 105 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 806bd1a7..1ea6e548 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -723,7 +723,6 @@ ostree_sign_add_pk ostree_sign_load_pk ostree_sign_set_pk ostree_sign_set_sk -ostree_sign_ed25519_keypair_generate ostree_sign_get_type ostree_sign_dummy_get_type diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 8be5a3bf..93f904b1 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -37,7 +37,6 @@ global: ostree_sign_set_sk; ostree_sign_dummy_get_type; ostree_sign_ed25519_get_type; - ostree_sign_ed25519_keypair_generate; } LIBOSTREE_2020.1; /* Stub section for the stable release *after* this development one; don't diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index c6c16302..1fb6ae05 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -38,12 +38,6 @@ #define OSTREE_SIGN_METADATA_ED25519_KEY "ostree.sign.ed25519" #define OSTREE_SIGN_METADATA_ED25519_TYPE "aay" -#if 0 -#define SIGNIFY_COMMENT_HEADER "untrusted comment:" -#define SIGNIFY_ID_LENGTH 8 -#define SIGNIFY_MAGIC_ED25519 "Ed" -#endif - struct _OstreeSignEd25519 { GObject parent; @@ -259,45 +253,6 @@ out: return ret; } -gboolean -ostree_sign_ed25519_keypair_generate (OstreeSign *self, - GVariant **out_secret_key, - GVariant **out_public_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)); - - if (sign->initialized != TRUE) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Not able to sign -- libsodium library isn't initialized properly"); - goto err; - } - -#ifdef HAVE_LIBSODIUM - unsigned char pk[crypto_sign_PUBLICKEYBYTES]; - unsigned char sk[crypto_sign_SECRETKEYBYTES]; - - if (crypto_sign_keypair(pk, sk)) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Not able to generate keypair"); - goto err; - } - - *out_secret_key = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, sk, crypto_sign_SECRETKEYBYTES, sizeof(guchar)); - *out_public_key = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, pk, crypto_sign_PUBLICKEYBYTES, sizeof(guchar)); - - return TRUE; -#endif /* HAVE_LIBSODIUM */ - -err: - return FALSE; -} - gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, GVariant *secret_key, GError **error) @@ -392,50 +347,6 @@ _load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, GError ** #ifdef HAVE_LIBSODIUM gboolean ret = FALSE; -#if 0 -/* Try to load the public key in signify format from the stream - * https://www.openbsd.org/papers/bsdcan-signify.html - * - * FIXME: Not sure if we need to support that format. - * */ - g_autofree gchar * comment = NULL; - while (TRUE) - { - gsize len = 0; - g_autofree char *line = g_data_input_stream_read_line (key_data_in, &len, NULL, error); - if (error) - goto err; - - if (line) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Signify format for ed25519 public key not found"); - goto err; - } - - if (comment == NULL) - { - /* Scan for the comment first and compare with prefix&suffix */ - if (g_str_has_prefix (line, SIGNIFY_COMMENT_HEADER) && g_str_has_suffix (line, "public key")) - /* Save comment without the prefix and blank space */ - comment = g_strdup (line + strlen(SIGNIFY_COMMENT_HEADER) + 1); - } - else - { - /* Read the key itself */ - /* base64 encoded key */ - gsize keylen = 0; - g_autofree guchar *key = g_base64_decode (line, &keylen); - - /* Malformed key */ - if (keylen != SIGNIFY_ID_LENGTH || - strncmp (line, SIGNIFY_MAGIC_ED25519, strlen(SIGNIFY_MAGIC_ED25519)) != 0) - continue; - - } - } -#endif /* 0 */ - /* Use simple file format with just a list of base64 public keys per line */ while (TRUE) { diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h index eb8f6701..16da4828 100644 --- a/src/libostree/ostree-sign-ed25519.h +++ b/src/libostree/ostree-sign-ed25519.h @@ -41,19 +41,19 @@ G_DECLARE_FINAL_TYPE (OstreeSignEd25519, gboolean ostree_sign_ed25519_data (OstreeSign *self, - GBytes *data, - GBytes **signature, - GCancellable *cancellable, - GError **error); + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error); const gchar * ostree_sign_ed25519_get_name (OstreeSign *self); const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self); const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self); gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error); + GBytes *data, + GVariant *signatures, + GError **error); gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, GVariant *secret_key, @@ -71,12 +71,5 @@ gboolean ostree_sign_ed25519_load_pk (OstreeSign *self, GVariant *options, GError **error); -_OSTREE_PUBLIC -gboolean ostree_sign_ed25519_keypair_generate (OstreeSign *self, - GVariant **out_secret_key, - GVariant **out_public_key, - GError **error); - - G_END_DECLS From bc4488692c68a2148b921efe94ea4dc2cee24725 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 7 Oct 2019 02:59:15 +0300 Subject: [PATCH 25/75] lib/sign: public API optimisation Removed from public `ostree_sign_detached_metadata_append` function. Renamed `metadata_verify` into `data_verify` to fit to real functionality. Signed-off-by: Denis Pynkin --- apidoc/ostree-sections.txt | 3 +- src/libostree/libostree-devel.sym | 3 +- src/libostree/ostree-repo-pull.c | 2 +- src/libostree/ostree-sign-dummy.c | 12 +++---- src/libostree/ostree-sign-dummy.h | 12 +++---- src/libostree/ostree-sign-ed25519.c | 54 ++++++++++++++--------------- src/libostree/ostree-sign-ed25519.h | 10 +++--- src/libostree/ostree-sign.c | 41 ++++++++++------------ src/libostree/ostree-sign.h | 29 +++++----------- 9 files changed, 75 insertions(+), 91 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 1ea6e548..440338c2 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -715,10 +715,9 @@ ostree_sign_list_names ostree_sign_commit ostree_sign_commit_verify ostree_sign_data +ostree_sign_data_verify ostree_sign_get_by_name ostree_sign_get_name -ostree_sign_detached_metadata_append -ostree_sign_metadata_verify ostree_sign_add_pk ostree_sign_load_pk ostree_sign_set_pk diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 93f904b1..3ca8f2c6 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -27,10 +27,9 @@ global: ostree_sign_commit; ostree_sign_commit_verify; ostree_sign_data; + ostree_sign_data_verify; ostree_sign_get_by_name; ostree_sign_get_name; - ostree_sign_detached_metadata_append; - ostree_sign_metadata_verify; ostree_sign_load_pk; ostree_sign_set_pk; ostree_sign_add_pk; diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index b87f6c90..1a4e64da 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1586,7 +1586,7 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, } /* Set return to true if any sign fit */ - if (ostree_sign_metadata_verify (sign, + if (ostree_sign_data_verify (sign, signed_data, signatures, &local_error diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index fb5a4f9e..e2d1fe56 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -54,13 +54,13 @@ ostree_sign_dummy_iface_init (OstreeSignInterface *self) { g_debug ("%s enter", __FUNCTION__); - self->data = ostree_sign_dummy_data; self->get_name = ostree_sign_dummy_get_name; + self->data = ostree_sign_dummy_data; + self->data_verify = ostree_sign_dummy_data_verify; self->metadata_key = ostree_sign_dummy_metadata_key; self->metadata_format = ostree_sign_dummy_metadata_format; - self->metadata_verify = ostree_sign_dummy_metadata_verify; - self->set_sk = ostree_sign_dummy_set_signature; - self->set_pk = ostree_sign_dummy_set_signature; + self->set_sk = ostree_sign_dummy_set_key; + self->set_pk = ostree_sign_dummy_set_key; } static void @@ -77,7 +77,7 @@ ostree_sign_dummy_init (OstreeSignDummy *self) self->signature_ascii = g_strdup(OSTREE_SIGN_DUMMY_SIGNATURE); } -gboolean ostree_sign_dummy_set_signature (OstreeSign *self, GVariant *key, GError **error) +gboolean ostree_sign_dummy_set_key (OstreeSign *self, GVariant *key, GError **error) { g_debug ("%s enter", __FUNCTION__); @@ -130,7 +130,7 @@ const gchar * ostree_sign_dummy_metadata_format (OstreeSign *self) return OSTREE_SIGN_METADATA_DUMMY_TYPE; } -gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, +gboolean ostree_sign_dummy_data_verify (OstreeSign *self, GBytes *data, GVariant *signatures, GError **error) diff --git a/src/libostree/ostree-sign-dummy.h b/src/libostree/ostree-sign-dummy.h index 847a7313..a0d10e1d 100644 --- a/src/libostree/ostree-sign-dummy.h +++ b/src/libostree/ostree-sign-dummy.h @@ -47,15 +47,15 @@ gboolean ostree_sign_dummy_data (OstreeSign *self, GCancellable *cancellable, GError **error); +gboolean ostree_sign_dummy_data_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error); + const gchar * ostree_sign_dummy_metadata_key (OstreeSign *self); const gchar * ostree_sign_dummy_metadata_format (OstreeSign *self); -gboolean ostree_sign_dummy_metadata_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error); - -gboolean ostree_sign_dummy_set_signature (OstreeSign *self, GVariant *key, GError **error); +gboolean ostree_sign_dummy_set_key (OstreeSign *self, GVariant *key, GError **error); G_END_DECLS diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 1fb6ae05..2bf10cf1 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -58,10 +58,10 @@ ostree_sign_ed25519_iface_init (OstreeSignInterface *self) g_debug ("%s enter", __FUNCTION__); self->data = ostree_sign_ed25519_data; + self->data_verify = ostree_sign_ed25519_data_verify; self->get_name = ostree_sign_ed25519_get_name; self->metadata_key = ostree_sign_ed25519_metadata_key; self->metadata_format = ostree_sign_ed25519_metadata_format; - self->metadata_verify = ostree_sign_ed25519_metadata_verify; self->set_sk = ostree_sign_ed25519_set_sk; self->set_pk = ostree_sign_ed25519_set_pk; self->add_pk = ostree_sign_ed25519_add_pk; @@ -138,32 +138,10 @@ err: return FALSE; } -const gchar * ostree_sign_ed25519_get_name (OstreeSign *self) -{ - g_debug ("%s enter", __FUNCTION__); - g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - - return OSTREE_SIGN_ED25519_NAME; -} - -const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self) -{ - g_debug ("%s enter", __FUNCTION__); - - return OSTREE_SIGN_METADATA_ED25519_KEY; -} - -const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) -{ - g_debug ("%s enter", __FUNCTION__); - - return OSTREE_SIGN_METADATA_ED25519_TYPE; -} - -gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error) +gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error) { g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); @@ -253,6 +231,28 @@ out: return ret; } +const gchar * ostree_sign_ed25519_get_name (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + + return OSTREE_SIGN_ED25519_NAME; +} + +const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + return OSTREE_SIGN_METADATA_ED25519_KEY; +} + +const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) +{ + g_debug ("%s enter", __FUNCTION__); + + return OSTREE_SIGN_METADATA_ED25519_TYPE; +} + gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, GVariant *secret_key, GError **error) diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h index 16da4828..4519961d 100644 --- a/src/libostree/ostree-sign-ed25519.h +++ b/src/libostree/ostree-sign-ed25519.h @@ -46,15 +46,15 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, GCancellable *cancellable, GError **error); +gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error); + const gchar * ostree_sign_ed25519_get_name (OstreeSign *self); const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self); const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self); -gboolean ostree_sign_ed25519_metadata_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error); - gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, GVariant *secret_key, GError **error); diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index 6e67acaa..b1975215 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -153,13 +153,26 @@ gboolean ostree_sign_data (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->data (self, data, signature, cancellable, error); } +gboolean +ostree_sign_data_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->data_verify != NULL, FALSE); + + return OSTREE_SIGN_GET_IFACE (self)->data_verify(self, data, signatures, error); +} + /* * Adopted version of _ostree_detached_metadata_append_gpg_sig () */ -GVariant * -ostree_sign_detached_metadata_append (OstreeSign *self, - GVariant *existing_metadata, - GBytes *signature_bytes) +static GVariant * +_sign_detached_metadata_append (OstreeSign *self, + GVariant *existing_metadata, + GBytes *signature_bytes) { g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (signature_bytes != NULL, FALSE); @@ -189,20 +202,6 @@ ostree_sign_detached_metadata_append (OstreeSign *self, return g_variant_dict_end (&metadata_dict); } - -gboolean -ostree_sign_metadata_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error) -{ - g_debug ("%s enter", __FUNCTION__); - g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->metadata_verify != NULL, FALSE); - - return OSTREE_SIGN_GET_IFACE (self)->metadata_verify(self, data, signatures, error); -} - gboolean ostree_sign_commit_verify (OstreeSign *self, OstreeRepo *repo, @@ -243,7 +242,7 @@ ostree_sign_commit_verify (OstreeSign *self, signature_format); - return ostree_sign_metadata_verify (self, + return ostree_sign_data_verify (self, signed_data, signatures, error); @@ -325,8 +324,6 @@ ostree_sign_commit (OstreeSign *self, error)) return glnx_prefix_error (error, "Failed to read detached metadata"); - // TODO: d4s: check if already signed? - commit_data = g_variant_get_data_as_bytes (commit_variant); if (!ostree_sign_data (self, commit_data, &signature, @@ -334,7 +331,7 @@ ostree_sign_commit (OstreeSign *self, return glnx_prefix_error (error, "Not able to sign the cobject"); new_metadata = - ostree_sign_detached_metadata_append (self, old_metadata, signature); + _sign_detached_metadata_append (self, old_metadata, signature); if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum, diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h index a9648cb1..008c3f9d 100644 --- a/src/libostree/ostree-sign.h +++ b/src/libostree/ostree-sign.h @@ -53,29 +53,24 @@ struct _OstreeSignInterface GBytes **signature, GCancellable *cancellable, GError **error); + gboolean (* data_verify) (OstreeSign *self, + GBytes *data, + GVariant *metadata, + GError **error); const gchar *(* metadata_key) (OstreeSign *self); const gchar *(* metadata_format) (OstreeSign *self); - gboolean (* metadata_verify) (OstreeSign *self, - GBytes *data, - GVariant *metadata, - GError **error); - gboolean (* set_sk) (OstreeSign *self, GVariant *secret_key, GError **error); - gboolean (* set_pk) (OstreeSign *self, GVariant *public_key, GError **error); - gboolean (* add_pk) (OstreeSign *self, GVariant *public_key, GError **error); - gboolean (* load_pk) (OstreeSign *self, GVariant *options, GError **error); - }; _OSTREE_PUBLIC @@ -88,6 +83,11 @@ gboolean ostree_sign_data (OstreeSign *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sign_data_verify (OstreeSign *self, + GBytes *data, + GVariant *signatures, + GError **error); _OSTREE_PUBLIC const gchar * ostree_sign_metadata_key (OstreeSign *self); @@ -95,11 +95,6 @@ const gchar * ostree_sign_metadata_key (OstreeSign *self); _OSTREE_PUBLIC const gchar * ostree_sign_metadata_format (OstreeSign *self); -_OSTREE_PUBLIC -GVariant * ostree_sign_detached_metadata_append (OstreeSign *self, - GVariant *existing_metadata, - GBytes *signature_bytes); - _OSTREE_PUBLIC gboolean ostree_sign_commit (OstreeSign *self, OstreeRepo *repo, @@ -107,12 +102,6 @@ gboolean ostree_sign_commit (OstreeSign *self, GCancellable *cancellable, GError **error); -_OSTREE_PUBLIC -gboolean ostree_sign_metadata_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error); - _OSTREE_PUBLIC gboolean ostree_sign_commit_verify (OstreeSign *self, OstreeRepo *repo, From f0181adff3d2a494944f86a9ec248d4763498045 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 7 Oct 2019 23:37:08 +0300 Subject: [PATCH 26/75] lib/sign: allow to add keys as base64 string for ed25519 Allow to add public and secret key for ed25519 module as based64 string. This allows to use common API for pulling and builtins without knowledge of used signature algorithm. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 32 +++----------------- src/libostree/ostree-sign-ed25519.c | 47 +++++++++++++++++++++++++++-- src/ostree/ot-builtin-commit.c | 12 +------- src/ostree/ot-builtin-sign.c | 45 ++++----------------------- 4 files changed, 56 insertions(+), 80 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 1a4e64da..f3f13ed3 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1567,20 +1567,7 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, &pk_ascii, NULL); if (pk_ascii != NULL) { - g_autoptr (GVariant) pk = NULL; - - if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) - { - // Just use the string as signature - pk = g_variant_new_string(pk_ascii); - } - else if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) - { - gsize key_len = 0; - g_autofree guchar *key = g_base64_decode (pk_ascii, &key_len); - pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); - } - + g_autoptr (GVariant) pk = g_variant_new_string(pk_ascii); if (!ostree_sign_set_pk (sign, pk, &local_error)) continue; } @@ -1976,18 +1963,8 @@ scan_commit_object (OtPullData *pull_data, { g_autoptr (GVariant) pk = NULL; - if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) - { - // Just use the string as signature - pk = g_variant_new_string(pk_ascii); - } - else if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) - { - gsize key_len = 0; - g_autofree guchar *key = g_base64_decode (pk_ascii, &key_len); - pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); - } - + // Just use the string as signature + pk = g_variant_new_string(pk_ascii); if (!ostree_sign_set_pk (sign, pk, &local_error)) continue; } @@ -4853,11 +4830,10 @@ ostree_repo_pull_with_options (OstreeRepo *self, else gpg_verify_state = (pull_data->gpg_verify ? "commit" : "disabled"); - g_string_append_printf (msg, "\nsecurity: GPG: %s ", gpg_verify_state); #else gpg_verify_state = "disabled"; - g_string_append_printf (msg, "\nsecurity: %s ", gpg_verify_state); #endif /* OSTREE_DISABLE_GPGME */ + g_string_append_printf (msg, "\nsecurity: GPG: %s ", gpg_verify_state); const char *sign_verify_state; sign_verify_state = (pull_data->sign_verify ? "commit" : "disabled"); diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 2bf10cf1..f90a310c 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -253,6 +253,10 @@ const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) return OSTREE_SIGN_METADATA_ED25519_TYPE; } +/* Support 2 representations: + * base64 ascii -- secret key is passed as string + * raw key -- key is passed as bytes array + * */ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, GVariant *secret_key, GError **error) @@ -266,7 +270,23 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, g_free (sign->secret_key); gsize n_elements = 0; - sign->secret_key = (guchar *) g_variant_get_fixed_array (secret_key, &n_elements, sizeof(guchar)); + + if (g_variant_is_of_type (secret_key, G_VARIANT_TYPE_STRING)) + { + const gchar *sk_ascii = g_variant_get_string (secret_key, NULL); + sign->secret_key = g_base64_decode (sk_ascii, &n_elements); + } + else if (g_variant_is_of_type (secret_key, G_VARIANT_TYPE_BYTESTRING)) + { + sign->secret_key = (guchar *) g_variant_get_fixed_array (secret_key, &n_elements, sizeof(guchar)); + } + else + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unknown ed25519 secret key type"); + goto err; + } + if (n_elements != crypto_sign_SECRETKEYBYTES) { @@ -282,6 +302,10 @@ err: return FALSE; } +/* Support 2 representations: + * base64 ascii -- public key is passed as string + * raw key -- key is passed as bytes array + * */ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, GVariant *public_key, GError **error) @@ -301,6 +325,10 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, return ostree_sign_ed25519_add_pk (self, public_key, error); } +/* Support 2 representations: + * base64 ascii -- public key is passed as string + * raw key -- key is passed as bytes array + * */ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, GVariant *public_key, GError **error) @@ -314,7 +342,22 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, gpointer key = NULL; gsize n_elements = 0; - key = (gpointer) g_variant_get_fixed_array (public_key, &n_elements, sizeof(guchar)); + + if (g_variant_is_of_type (public_key, G_VARIANT_TYPE_STRING)) + { + const gchar *pk_ascii = g_variant_get_string (public_key, NULL); + key = g_base64_decode (pk_ascii, &n_elements); + } + else if (g_variant_is_of_type (public_key, G_VARIANT_TYPE_BYTESTRING)) + { + key = (gpointer) g_variant_get_fixed_array (public_key, &n_elements, sizeof(guchar)); + } + else + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unknown ed25519 public key type"); + goto err; + } hex = g_malloc0 (crypto_sign_PUBLICKEYBYTES*2 + 1); g_debug ("Read ed25519 public key = %s", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, key, n_elements)); diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c index 7d412639..606af2be 100644 --- a/src/ostree/ot-builtin-commit.c +++ b/src/ostree/ot-builtin-commit.c @@ -855,17 +855,7 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio const char *keyid = *iter; g_autoptr (GVariant) secret_key = NULL; - if (!g_strcmp0(ostree_sign_get_name (sign), "dummy")) - { - secret_key = g_variant_new_string (keyid); - } - else if (!g_strcmp0 (ostree_sign_get_name (sign), "ed25519")) - { - gsize key_len = 0; - g_autofree guchar *key = g_base64_decode (keyid, &key_len); - - secret_key = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); - } + secret_key = g_variant_new_string (keyid); if (!ostree_sign_set_sk (sign, secret_key, error)) goto out; diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index b1c9a73b..f673631d 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -72,11 +72,6 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, char **key_ids; int n_key_ids, ii; gboolean ret = FALSE; -#if defined(HAVE_LIBSODIUM) - g_autoptr (GVariant) ed25519_sk = NULL; - g_autoptr (GVariant) ed25519_pk = NULL; -#endif - context = g_option_context_new ("COMMIT KEY-ID..."); @@ -119,25 +114,14 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, { g_autoptr (GVariant) sk = NULL; g_autoptr (GVariant) pk = NULL; - g_autofree guchar *key = NULL; - if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) - { - // Just use the string as signature - sk = g_variant_new_string(key_ids[ii]); - pk = g_variant_new_string(key_ids[ii]); - } if (opt_verify) { g_autoptr (GError) local_error = NULL; - if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) - { - gsize key_len = 0; - g_autofree guchar *key = g_base64_decode (key_ids[ii], &key_len); - pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); - } + // Pass the key as a string + pk = g_variant_new_string(key_ids[ii]); if (!ostree_sign_set_pk (sign, pk, &local_error)) continue; @@ -151,13 +135,8 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } else { - if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) - { - gsize key_len = 0; - g_autofree guchar *key = g_base64_decode (key_ids[ii], &key_len); - sk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); - } - + // Pass the key as a string + sk = g_variant_new_string(key_ids[ii]); if (!ostree_sign_set_sk (sign, sk, error)) { ret = FALSE; @@ -238,20 +217,8 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, break; - if (!g_strcmp0(ostree_sign_get_name(sign), "dummy")) - { - // Just use the string as signature - sk = g_variant_new_string(line); - } - - - if (!g_strcmp0(ostree_sign_get_name(sign), "ed25519")) - { - gsize key_len = 0; - g_autofree guchar *key = g_base64_decode (line, &key_len); - sk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); - } - + // Pass the key as a string + sk = g_variant_new_string(line); if (!ostree_sign_set_sk (sign, sk, error)) { ret = FALSE; From 7fa7c3c4f94ebd0e17fa831e47e53cf4aa3e94e0 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 8 Oct 2019 01:55:25 +0300 Subject: [PATCH 27/75] sign: use common function for loading public keys during pulling Add function `_load_public_keys()` to pre-load public keys according remote's configuration. If no keys configured for remote, then use system-wide configuration. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 184 +++++++++++++++++++------------ 1 file changed, 113 insertions(+), 71 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index f3f13ed3..4dd0f11a 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1469,6 +1469,104 @@ process_verify_result (OtPullData *pull_data, } #endif /* OSTREE_DISABLE_GPGME */ +/* _remote_load_public_keys: + * + * Load public keys according remote's configuration: + * inlined key passed via config option `verification-key` or + * file name with public keys via `verification-file` option. + * + * If both options are set then load all all public keys + * both from file and inlined in config. + * + * Returns: %FALSE if any source is configured but nothing has been loaded. + * Returns: %TRUE if no configuration or any key loaded. + * */ +static gboolean +_load_public_keys (OtPullData *pull_data, + OstreeSign *sign) +{ + + g_autofree gchar *pk_ascii = NULL; + g_autofree gchar *pk_file = NULL; + gboolean loaded_from_file = TRUE; + gboolean loaded_inlined = TRUE; + g_autoptr (GError) error = NULL; + + /* Load keys for remote from file */ + ostree_repo_get_remote_option (pull_data->repo, + pull_data->remote_name, + "verification-file", NULL, + &pk_file, NULL); + + ostree_repo_get_remote_option (pull_data->repo, + pull_data->remote_name, + "verification-key", NULL, + &pk_ascii, NULL); + + /* return TRUE if there is no configuration for remote */ + if ((pk_file == NULL) &&(pk_ascii == NULL)) + { + /* It is expected what remote may have verification file as + * a part of configuration. Hence there is not a lot of sense + * for automatic resolve of per-remote keystore file as it + * used in find_keyring () for GPG. + * If it is needed to add the similar mechanism, it is preferable + * to pass the path to ostree_sign_load_pk () via GVariant options + * and call it here for loading with method and file structure + * specific for signature type. + */ + return TRUE; + } + + if (pk_file != NULL) + { + g_autoptr (GVariantBuilder) builder = NULL; + g_autoptr (GVariant) options = NULL; + + builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); + options = g_variant_builder_end (builder); + + if (ostree_sign_load_pk (sign, options, &error)) + loaded_from_file = TRUE; + else + { + if (error == NULL) + g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, + "unknown reason"); + + g_warning("Unable to load public keys from file '%s': %s", + pk_file, error->message); + g_clear_error (&error); + } + } + + if (pk_ascii != NULL) + { + g_autoptr (GVariant) pk = g_variant_new_string(pk_ascii); + + /* Add inlined public key */ + if (loaded_from_file) + loaded_inlined = ostree_sign_add_pk (sign, pk, &error); + else + loaded_inlined = ostree_sign_set_pk (sign, pk, &error); + + if (!loaded_inlined) + { + if (error == NULL) + g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, + "unknown reason"); + + g_warning("Unable to load public key '%s': %s", + pk_ascii, error->message); + g_clear_error (&error); + } + } + + /* Return true if able to load from any source */ + return (loaded_from_file || loaded_inlined); +} + static gboolean ostree_verify_unwritten_commit (OtPullData *pull_data, const char *checksum, @@ -1518,17 +1616,16 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, gboolean ret = FALSE; g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); + /* list all signature types in detached metadata and check if signed by any? */ g_auto (GStrv) names = ostree_sign_list_names(); for (guint i=0; i < g_strv_length (names); i++) { g_autoptr (OstreeSign) sign = NULL; - g_autoptr (GError) local_error = NULL; g_autoptr (GVariant) signatures = NULL; const gchar *signature_key = NULL; GVariantType *signature_format = NULL; - g_autofree gchar *pk_ascii = NULL; - g_autofree gchar *pk_file = NULL; + g_autoptr (GError) local_error = NULL; if ((sign = ostree_sign_get_by_name (names[i], &local_error)) == NULL) continue; @@ -1539,45 +1636,21 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, signatures = g_variant_lookup_value (detached_metadata, signature_key, signature_format); + + /* If not found signatures for requested signature subsystem */ if (!signatures) continue; - /* Load keys for remote from file */ - ostree_repo_get_remote_option (pull_data->repo, - pull_data->remote_name, - "verification-file", NULL, - &pk_file, NULL); - if (pk_file != NULL) - { - g_autoptr (GVariantBuilder) builder = NULL; - g_autoptr (GVariant) options = NULL; + /* Try to load public key(s) according remote's configuration */ + if (!_load_public_keys (pull_data, sign)) + continue; - builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); - options = g_variant_builder_end (builder); - - if (!ostree_sign_load_pk (sign, options, &local_error)) - g_clear_error (&local_error); - } - - /* Override key if it is set explicitly */ - ostree_repo_get_remote_option (pull_data->repo, - pull_data->remote_name, - "verification-key", NULL, - &pk_ascii, NULL); - if (pk_ascii != NULL) - { - g_autoptr (GVariant) pk = g_variant_new_string(pk_ascii); - if (!ostree_sign_set_pk (sign, pk, &local_error)) - continue; - } - - /* Set return to true if any sign fit */ + /* Return true if any signature fit to pre-loaded public keys. + * If no keys configured -- then system configuration will be used */ if (ostree_sign_data_verify (sign, - signed_data, - signatures, - &local_error - )) + signed_data, + signatures, + &local_error)) ret = TRUE; } @@ -1931,44 +2004,13 @@ scan_commit_object (OtPullData *pull_data, { g_autoptr (OstreeSign) sign = NULL; g_autoptr (GError) local_error = NULL; - g_autofree gchar *pk_ascii = NULL; - g_autofree gchar *pk_file = NULL; if ((sign = ostree_sign_get_by_name (*iter, &local_error)) == NULL) continue; - /* Load keys for remote from file */ - ostree_repo_get_remote_option (pull_data->repo, - pull_data->remote_name, - "verification-file", NULL, - &pk_file, NULL); - if (pk_file != NULL) - { - g_autoptr (GVariantBuilder) builder = NULL; - g_autoptr (GVariant) options = NULL; - - builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); - options = g_variant_builder_end (builder); - - if (!ostree_sign_load_pk (sign, options, &local_error)) - g_clear_error (&local_error); - } - - ostree_repo_get_remote_option (pull_data->repo, - pull_data->remote_name, - "verification-key", NULL, - &pk_ascii, NULL); - if (pk_ascii != NULL) - { - g_autoptr (GVariant) pk = NULL; - - // Just use the string as signature - pk = g_variant_new_string(pk_ascii); - if (!ostree_sign_set_pk (sign, pk, &local_error)) - continue; - } - + /* Try to load public key(s) according remote's configuration */ + if (!_load_public_keys (pull_data, sign)) + continue; /* Set return to true if any sign fit */ if (ostree_sign_commit_verify (sign, From eb8e501ecef550191d60b53c7130988a3509e711 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 27 Oct 2019 21:21:21 +0300 Subject: [PATCH 28/75] lib/sign: minor optimisation for ed25519 Exclude unneeded conversion while load keys from files. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-ed25519.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index f90a310c..662521b9 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -405,10 +405,7 @@ _load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, GError ** /* Read the key itself */ /* base64 encoded key */ - gsize key_len = 0; - g_autofree guchar *key = g_base64_decode (line, &key_len); - - pk = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, key, key_len, sizeof(guchar)); + pk = g_variant_new_string (line); if (ostree_sign_ed25519_add_pk (self, pk, error)) { ret = TRUE; From ceaf6d7f546690601df98ee5e9d971f5b31096a9 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 27 Oct 2019 19:45:48 +0000 Subject: [PATCH 29/75] lib/sign: add ostree_seign_clear_keys function Add the function for implicit cleanup of all loaded keys. Signed-off-by: Denis Pynkin --- apidoc/ostree-sections.txt | 1 + src/libostree/libostree-devel.sym | 1 + src/libostree/ostree-sign-ed25519.c | 40 ++++++++++++++++++++++++----- src/libostree/ostree-sign-ed25519.h | 3 +++ src/libostree/ostree-sign.c | 11 ++++++++ src/libostree/ostree-sign.h | 6 +++++ 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 440338c2..9b71d610 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -719,6 +719,7 @@ ostree_sign_data_verify ostree_sign_get_by_name ostree_sign_get_name ostree_sign_add_pk +ostree_sign_clear_keys ostree_sign_load_pk ostree_sign_set_pk ostree_sign_set_sk diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 3ca8f2c6..a10ec266 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -30,6 +30,7 @@ global: ostree_sign_data_verify; ostree_sign_get_by_name; ostree_sign_get_name; + ostree_sign_clear_keys; ostree_sign_load_pk; ostree_sign_set_pk; ostree_sign_add_pk; diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 662521b9..f61c3bdd 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -62,6 +62,7 @@ ostree_sign_ed25519_iface_init (OstreeSignInterface *self) self->get_name = ostree_sign_ed25519_get_name; self->metadata_key = ostree_sign_ed25519_metadata_key; self->metadata_format = ostree_sign_ed25519_metadata_format; + self->clear_keys = ostree_sign_ed25519_clear_keys; self->set_sk = ostree_sign_ed25519_set_sk; self->set_pk = ostree_sign_ed25519_set_pk; self->add_pk = ostree_sign_ed25519_add_pk; @@ -253,6 +254,36 @@ const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) return OSTREE_SIGN_METADATA_ED25519_TYPE; } +gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + +#ifdef HAVE_LIBSODIUM + OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + + /* Clear secret key */ + if (sign->secret_key != NULL) + { + memset (sign->secret_key, 0, crypto_sign_SECRETKEYBYTES); + g_free (sign->secret_key); + sign->secret_key = NULL; + } + + /* Clear already loaded trusted keys */ + if (sign->public_keys != NULL) + { + g_list_free_full (sign->public_keys, g_free); + sign->public_keys = NULL; + } + + return TRUE; + +#endif /* HAVE_LIBSODIUM */ + return FALSE; +} + /* Support 2 representations: * base64 ascii -- secret key is passed as string * raw key -- key is passed as bytes array @@ -267,7 +298,7 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, #ifdef HAVE_LIBSODIUM OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); - g_free (sign->secret_key); + ostree_sign_ed25519_clear_keys (self, error); gsize n_elements = 0; @@ -315,12 +346,7 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); - /* Substitute the key(s) with a new one */ - if (sign->public_keys != NULL) - { - g_list_free_full (sign->public_keys, g_free); - sign->public_keys = NULL; - } + ostree_sign_ed25519_clear_keys (self, error); return ostree_sign_ed25519_add_pk (self, public_key, error); } diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h index 4519961d..bced1cdf 100644 --- a/src/libostree/ostree-sign-ed25519.h +++ b/src/libostree/ostree-sign-ed25519.h @@ -55,6 +55,9 @@ const gchar * ostree_sign_ed25519_get_name (OstreeSign *self); const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self); const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self); +gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, + GError **error); + gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, GVariant *secret_key, GError **error); diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index b1975215..e5d55ef2 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -87,6 +87,17 @@ const gchar * ostree_sign_metadata_format (OstreeSign *self) return OSTREE_SIGN_GET_IFACE (self)->metadata_format (self); } +gboolean ostree_sign_clear_keys (OstreeSign *self, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + if (OSTREE_SIGN_GET_IFACE (self)->clear_keys == NULL) + return TRUE; + + return OSTREE_SIGN_GET_IFACE (self)->clear_keys (self, error); +} + gboolean ostree_sign_set_sk (OstreeSign *self, GVariant *secret_key, GError **error) diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h index 008c3f9d..9add0450 100644 --- a/src/libostree/ostree-sign.h +++ b/src/libostree/ostree-sign.h @@ -59,6 +59,8 @@ struct _OstreeSignInterface GError **error); const gchar *(* metadata_key) (OstreeSign *self); const gchar *(* metadata_format) (OstreeSign *self); + gboolean (* clear_keys) (OstreeSign *self, + GError **error); gboolean (* set_sk) (OstreeSign *self, GVariant *secret_key, GError **error); @@ -109,6 +111,10 @@ gboolean ostree_sign_commit_verify (OstreeSign *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sign_clear_keys (OstreeSign *self, + GError **error); + _OSTREE_PUBLIC gboolean ostree_sign_set_sk (OstreeSign *self, GVariant *secret_key, From ee12b7e7743d695c06c1ffbe1d76bcb53975f83b Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 27 Oct 2019 23:15:10 +0300 Subject: [PATCH 30/75] 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 --- src/libostree/ostree-sign-ed25519.c | 258 +++++++++++++++++++++------- 1 file changed, 196 insertions(+), 62 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; - - /* Clear already loaded keys */ - if (sign->public_keys != NULL) - { - g_list_free_full (sign->public_keys, g_free); - sign->public_keys = 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")); } - /* Read only file provided */ - if (g_variant_lookup (options, "filename", "&s", &filename)) - return _load_pk_from_file (self, filename, error); - - /* 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; - - /* Scan all well-known files and directories */ - for (gint i=0; i < G_N_ELEMENTS(default_dirs); i++) + /* 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_autoptr (GDir) dir = g_dir_open (default_dirs[i], 0, error); + gchar *base_name = NULL; + g_autofree gchar *base_dir = NULL; + g_autoptr (GDir) dir = NULL; + + base_name = g_build_filename ((gchar *)g_ptr_array_index (base_dirs, i), + trusted ? "trusted.ed25519" : "revoked.ed25519", + NULL); + + g_debug ("Check ed25519 keys from file: %s", base_name); + g_ptr_array_add (ed25519_files, base_name); + + 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; +} From 200efd7d44cfc490d2bbca7e09d494e851e6c890 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 29 Oct 2019 22:16:09 +0300 Subject: [PATCH 31/75] builtin/sign: add option 'keys-dir' Option '--keys-dir' is used for redefinition of default directories with public/revoked keys. If keys directory is set then default directories are ignored and target directory is expected to contain following structure for ed25519 signature mechanism: dir/ trusted.ed25519 <- file with trusted keys revoked.ed25519 <- file with revoked keys trusted.ed25519.d/ <- directory with files containing trusted keys revoked.ed25519.d/ <- directory with files containing revoked keys Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-sign.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ostree/ot-builtin-sign.c b/src/ostree/ot-builtin-sign.c index f673631d..73561b43 100644 --- a/src/ostree/ot-builtin-sign.c +++ b/src/ostree/ot-builtin-sign.c @@ -37,6 +37,7 @@ static gboolean opt_delete; static gboolean opt_verify; static char *opt_sign_name; static char *opt_filename; +static char *opt_keysdir; /* ATTENTION: * Please remember to update the bash-completion script (bash/ostree) and @@ -48,9 +49,10 @@ static GOptionEntry options[] = { { "verify", 0, 0, G_OPTION_ARG_NONE, &opt_verify, "Verify signatures", NULL}, { "sign-type", 's', 0, G_OPTION_ARG_STRING, &opt_sign_name, "Signature type to use (defaults to 'ed25519')", "NAME"}, #if defined(HAVE_LIBSODIUM) - { "keys-file", 's', 0, G_OPTION_ARG_STRING, &opt_filename, "Read key(s) from file", "NAME"}, + { "keys-file", 0, 0, G_OPTION_ARG_STRING, &opt_filename, "Read key(s) from file", "NAME"}, + { "keys-dir", 0, 0, G_OPTION_ARG_STRING, &opt_keysdir, "Redefine system-wide directories with public and revoked keys for verification", "NAME"}, #endif - { NULL } + { NULL } }; static void @@ -131,7 +133,10 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, resolved_commit, cancellable, &local_error)) - ret = TRUE; + { + ret = TRUE; + goto out; + } } else { @@ -162,6 +167,9 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, g_autoptr (GVariant) options = NULL; builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + /* Use custom directory with public and revoked keys instead of system-wide directories */ + if (opt_keysdir) + g_variant_builder_add (builder, "{sv}", "basedir", g_variant_new_string (opt_keysdir)); /* The last chance for verification source -- system files */ if (opt_filename) g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (opt_filename)); @@ -235,9 +243,8 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation, } } } - // No valid signature found - if (opt_verify && (ret != TRUE)) + if (opt_verify && (ret != TRUE) && (*error == NULL)) g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No valid signatures found"); From 7e71f87ebc11fb930e7c42d8db263b1fe6073e98 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 29 Oct 2019 22:23:55 +0300 Subject: [PATCH 32/75] tests/sign: check system-wide config and revoked keys Extend the ed25519 tests with checking the system-wide directory keys loading code(with the help of redefinition). Added test of ed25519 revoking keys mechanism. Signed-off-by: Denis Pynkin --- tests/test-signed-commit.sh | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh index e4138817..c523aedd 100755 --- a/tests/test-signed-commit.sh +++ b/tests/test-signed-commit.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..8" +echo "1..10" mkdir ${test_tmpdir}/repo ostree_repo_init repo --mode="archive" @@ -58,6 +58,8 @@ if ! has_libsodium; then echo "ok multiple signing # SKIP due libsodium unavailability" echo "ok verify ed25519 keys file # SKIP due libsodium unavailability" echo "ok sign with ed25519 keys file # SKIP due libsodium unavailability" + echo "ok verify ed25519 system-wide configuration # SKIP due libsodium unavailability" + echo "ok verify ed25519 revoking keys mechanism # SKIP due libsodium unavailability" exit 0 fi @@ -125,10 +127,12 @@ PUBKEYS="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.ed25519)" if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT}; then exit 1 fi + # Test if have a problem with file object if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${test_tmpdir} ${COMMIT}; then exit 1 fi + # Test with single key in list echo ${PUBLIC} > ${PUBKEYS} ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} @@ -169,3 +173,27 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=ed25519 --keys- ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT} echo "ok sign with ed25519 keys file" +# Check the well-known places mechanism +mkdir -p ${test_tmpdir}/{trusted,revoked}.ed25519.d +for((i=0;i<100;i++)); do + # Generate some key files with random public signatures + openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 > ${test_tmpdir}/trusted.ed25519.d/signature_$i +done +# Check no valid public keys are available +if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-dir=${test_tmpdir} ${COMMIT}; then + exit 1 +fi +echo ${PUBLIC} > ${test_tmpdir}/trusted.ed25519.d/correct +# Verify with correct key +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-dir=${test_tmpdir} ${COMMIT} + +echo "ok verify ed25519 system-wide configuration" + +# Add the public key into revoked list +echo ${PUBLIC} > ${test_tmpdir}/revoked.ed25519.d/correct +# Check if public key is not valid anymore +if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-dir=${test_tmpdir} ${COMMIT}; then + exit 1 +fi +rm -rf ${test_tmpdir}/{trusted,revoked}.ed25519.d +echo "ok verify ed25519 revoking keys mechanism" From 0c89055b191a0dd90489418402a51c20e5f1f935 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 1 Nov 2019 02:44:25 +0300 Subject: [PATCH 33/75] man: document `ostree sign` Add man page for `ostree sign`. Signed-off-by: Denis Pynkin --- Makefile-man.am | 2 +- man/ostree-sign.xml | 152 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 man/ostree-sign.xml diff --git a/Makefile-man.am b/Makefile-man.am index bc58103b..718e773c 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -32,7 +32,7 @@ ostree-commit.1 ostree-create-usb.1 ostree-export.1 \ ostree-config.1 ostree-diff.1 ostree-find-remotes.1 ostree-fsck.1 \ ostree-init.1 ostree-log.1 ostree-ls.1 ostree-prune.1 ostree-pull-local.1 \ ostree-pull.1 ostree-refs.1 ostree-remote.1 ostree-reset.1 \ -ostree-rev-parse.1 ostree-show.1 ostree-summary.1 \ +ostree-rev-parse.1 ostree-show.1 ostree-sign.1 ostree-summary.1 \ ostree-static-delta.1 if USE_LIBSOUP man1_files += ostree-trivial-httpd.1 diff --git a/man/ostree-sign.xml b/man/ostree-sign.xml new file mode 100644 index 00000000..50c0b337 --- /dev/null +++ b/man/ostree-sign.xml @@ -0,0 +1,152 @@ + + + + + + + + + ostree sign + OSTree + + + + Developer + Colin + Walters + walters@verbum.org + + + + + + ostree sign + 1 + + + + ostree-sign + Sign a commit + + + + + ostree sign OPTIONS COMMIT KEY-ID + + + + + Description + + + Add a new signature to a commit. + + Note that currently, this will append a new signature even if + the commit is already signed with a given key. + + + + There are several "well-known" system places for `ed25519` trusted and revoked public keys -- expected single base64-encoded key per line. + + + Files: + + /etc/ostree/trusted.ed25519 + /etc/ostree/revoked.ed25519 + /usr/share/ostree/trusted.ed25519 + /usr/share/ostree/revoked.ed25519 + + + + Directories containing files with keys: + + /etc/ostree/trusted.ed25519.d + /etc/ostree/revoked.ed25519.d + /usr/share/ostree/trusted.ed25519.d + /usr/share/ostree/rvokeded.ed25519.d + + + + + + Options + + + + + + + + + + base64-encoded secret (for signing) or public key (for verifying). + + + + + + + ASCII-string used as secret key and public key. + + + + + + + + + Verify signatures + + + + + + Use particular signature mechanism. Currently + available ed25519 and dummy + signature types. + + The default is ed25519. + + + + + + Read key(s) from file filename. + + + + Valid for ed25519 signature type. + For ed25519 this file must contain base64-encoded + secret key(s) (for signing) or public key(s) (for verifying) per line. + + + + + + Redefine the system path, where to search files and subdirectories with + well-known and revoked keys. + + + + + From e9b1ebfc1152ca49106021a08865da89acfc1512 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 1 Nov 2019 03:00:13 +0300 Subject: [PATCH 34/75] bash-completion: add completion for `ostree sign` Add bash completion with supported options for signing command. Signed-off-by: Denis Pynkin --- bash/ostree | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/bash/ostree b/bash/ostree index 4aec588b..7256e40a 100644 --- a/bash/ostree +++ b/bash/ostree @@ -1484,6 +1484,48 @@ _ostree_show() { return 0 } +_ostree_sign() { + local boolean_options=" + $main_boolean_options + --delete -d + --verify -v + " + + local options_with_args=" + --sign-type + --keys-file + --keys-dir + --repo + " + + local options_with_args_glob=$( __ostree_to_extglob "$options_with_args" ) + + case "$prev" in + --keys-file|--keys-dir|--repo) + __ostree_compreply_dirs_only + return 0 + ;; + $options_with_args_glob ) + return 0 + ;; + esac + + case "$cur" in + -*) + local all_options="$boolean_options $options_with_args" + __ostree_compreply_all_options + ;; + *) + local argpos=$( __ostree_pos_first_nonflag $( __ostree_to_alternatives "$options_with_args" ) ) + + if [ $cword -eq $argpos ]; then + __ostree_compreply_commits + fi + esac + + return 0 +} + _ostree_static_delta_apply_offline() { local boolean_options=" $main_boolean_options @@ -1747,6 +1789,7 @@ _ostree() { reset rev-parse show + sign static-delta summary " From 908a2cd7601491195db6c4e6151a601b002c25e0 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 10 Nov 2019 16:51:23 +0300 Subject: [PATCH 35/75] apidoc: add API documentation for signing interface Add the documentation for all public functions. Signed-off-by: Denis Pynkin --- apidoc/ostree-docs.xml | 1 + src/libostree/ostree-sign.c | 355 +++++++++++++++++++++++++++++------- src/libostree/ostree-sign.h | 30 +-- 3 files changed, 298 insertions(+), 88 deletions(-) diff --git a/apidoc/ostree-docs.xml b/apidoc/ostree-docs.xml index 8721ffa8..1ad0de37 100644 --- a/apidoc/ostree-docs.xml +++ b/apidoc/ostree-docs.xml @@ -21,6 +21,7 @@ + diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index e5d55ef2..95319f67 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -22,6 +22,15 @@ * */ +/** + * SECTION:ostree-sign + * @title: Signature management + * @short_description: Sign and verify commits + * + * An #OstreeSign interface allows to select and use any available engine + * for signing or verifying the commit object or summary file. + */ + #include "config.h" #include @@ -71,7 +80,20 @@ ostree_sign_default_init (OstreeSignInterface *iface) g_debug ("OstreeSign initialization"); } -const gchar * ostree_sign_metadata_key (OstreeSign *self) +/** + * ostree_sign_metadata_key: + * @self: an #OstreeSign object + * + * Return the pointer to the name of the key used in (detached) metadata for + * current signing engine. + * + * Returns: (transfer none): pointer to the metadata key name, + * @NULL in case of error (unlikely). + * + * Since: 2020.2 + */ +const gchar * +ostree_sign_metadata_key (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); @@ -79,7 +101,20 @@ const gchar * ostree_sign_metadata_key (OstreeSign *self) return OSTREE_SIGN_GET_IFACE (self)->metadata_key (self); } -const gchar * ostree_sign_metadata_format (OstreeSign *self) +/** + * ostree_sign_metadata_format: + * @self: an #OstreeSign object + * + * Return the pointer to the string with format used in (detached) metadata for + * current signing engine. + * + * Returns: (transfer none): pointer to the metadata format, + * @NULL in case of error (unlikely). + * + * Since: 2020.2 + */ +const gchar * +ostree_sign_metadata_format (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); @@ -87,8 +122,20 @@ const gchar * ostree_sign_metadata_format (OstreeSign *self) return OSTREE_SIGN_GET_IFACE (self)->metadata_format (self); } -gboolean ostree_sign_clear_keys (OstreeSign *self, - GError **error) +/** + * ostree_sign_clear_keys: + * @self: an #OstreeSign object + * @error: a #GError + * + * Clear all previously preloaded secret and public keys. + * + * Returns: @TRUE in case if no errors, @FALSE in case of error + * + * Since: 2020.2 + */ +gboolean +ostree_sign_clear_keys (OstreeSign *self, + GError **error) { g_debug ("%s enter", __FUNCTION__); @@ -98,9 +145,25 @@ gboolean ostree_sign_clear_keys (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->clear_keys (self, error); } -gboolean ostree_sign_set_sk (OstreeSign *self, - GVariant *secret_key, - GError **error) +/** + * ostree_sign_set_sk: + * @self: an #OstreeSign object + * @secret_key: secret key to be added + * @error: a #GError + * + * Set the secret key to be used for signing data, commits and summary. + * + * The @secret_key argument depends of the particular engine implementation. + * + * Returns: @TRUE in case if the key could be set successfully, + * @FALSE in case of error (@error will contain the reason). + * + * Since: 2020.2 + */ +gboolean +ostree_sign_set_sk (OstreeSign *self, + GVariant *secret_key, + GError **error) { g_debug ("%s enter", __FUNCTION__); @@ -110,9 +173,26 @@ gboolean ostree_sign_set_sk (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->set_sk (self, secret_key, error); } -gboolean ostree_sign_set_pk (OstreeSign *self, - GVariant *public_key, - GError **error) +/** + * ostree_sign_set_pk: + * @self: an #OstreeSign object + * @public_key: single public key to be added + * @error: a #GError + * + * Set the public key for verification. It is expected what all + * previously pre-loaded public keys will be dropped. + * + * The @public_key argument depends of the particular engine implementation. + * + * Returns: @TRUE in case if the key could be set successfully, + * @FALSE in case of error (@error will contain the reason). + * + * Since: 2020.2 + */ +gboolean +ostree_sign_set_pk (OstreeSign *self, + GVariant *public_key, + GError **error) { g_debug ("%s enter", __FUNCTION__); @@ -122,9 +202,26 @@ gboolean ostree_sign_set_pk (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->set_pk (self, public_key, error); } -gboolean ostree_sign_add_pk (OstreeSign *self, - GVariant *public_key, - GError **error) +/** + * ostree_sign_add_pk: + * @self: an #OstreeSign object + * @public_key: single public key to be added + * @error: a #GError + * + * Add the public key for verification. Could be called multiple times for + * adding all needed keys to be used for verification. + * + * The @public_key argument depends of the particular engine implementation. + * + * Returns: @TRUE in case if the key could be added successfully, + * @FALSE in case of error (@error will contain the reason). + * + * Since: 2020.2 + */ +gboolean +ostree_sign_add_pk (OstreeSign *self, + GVariant *public_key, + GError **error) { g_debug ("%s enter", __FUNCTION__); @@ -134,9 +231,33 @@ gboolean ostree_sign_add_pk (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->add_pk (self, public_key, error); } -/* Load private keys for verification from anywhere. - * No need to have the same function for secret keys -- the signing SW must do it in it's own way - * */ +/** + * ostree_sign_load_pk: + * @self: an #OstreeSign object + * @options: any options + * @error: a #GError + * + * Load public keys for verification from anywhere. + * It is expected that all keys would be added to already pre-loaded keys. + * + * The @options argument depends of the particular engine implementation. + * + * For example, @ed25515 engine could use following string-formatted options: + * - @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. + * + * Returns: @TRUE in case if at least one key could be load successfully, + * @FALSE in case of error (@error will contain the reason). + * + * Since: 2020.2 + */ +/* + * No need to have similar function for secret keys load -- it is expected + * what the signing software will load the secret key in it's own way. + */ gboolean ostree_sign_load_pk (OstreeSign *self, GVariant *options, @@ -150,11 +271,30 @@ ostree_sign_load_pk (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->load_pk (self, options, error); } -gboolean ostree_sign_data (OstreeSign *self, - GBytes *data, - GBytes **signature, - GCancellable *cancellable, - GError **error) +/** + * ostree_sign_data: + * @self: an #OstreeSign object + * @data: the raw data to be signed with pre-loaded secret key + * @signature: in case of success will contain signature + * @cancellable: A #GCancellable + * @error: a #GError + * + * Sign the given @data with pre-loaded secret key. + * + * Depending of the signing engine used you will need to load + * the secret key with #ostree_sign_set_sk. + * + * Returns: @TRUE if @data has been signed successfully, + * @FALSE in case of error (@error will contain the reason). + * + * Since: 2020.2 + */ +gboolean +ostree_sign_data (OstreeSign *self, + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error) { g_debug ("%s enter", __FUNCTION__); @@ -164,11 +304,29 @@ gboolean ostree_sign_data (OstreeSign *self, return OSTREE_SIGN_GET_IFACE (self)->data (self, data, signature, cancellable, error); } +/** + * ostree_sign_data_verify: + * @self: an #OstreeSign object + * @data: the raw data to check + * @signatures: the signatures to be checked + * @error: a #GError + * + * Verify given data against signatures with pre-loaded public keys. + * + * Depending of the signing engine used you will need to load + * the public key(s) with #ostree_sign_set_pk, #ostree_sign_add_pk + * or #ostree_sign_load_pk. + * + * Returns: @TRUE if @data has been signed at least with any single valid key, + * @FALSE in case of error or no valid keys are available (@error will contain the reason). + * + * Since: 2020.2 + */ gboolean ostree_sign_data_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error) + GBytes *data, + GVariant *signatures, + GError **error) { g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); @@ -213,6 +371,25 @@ _sign_detached_metadata_append (OstreeSign *self, return g_variant_dict_end (&metadata_dict); } +/** + * ostree_sign_commit_verify: + * @self: an #OstreeSign object + * @repo: an #OsreeRepo object + * @commit_checksum: SHA256 of given commit to verify + * @cancellable: A #GCancellable + * @error: a #GError + * + * Verify if commit is signed with known key. + * + * Depending of the signing engine used you will need to load + * the public key(s) for verification with #ostree_sign_set_pk, + * #ostree_sign_add_pk and/or #ostree_sign_load_pk. + * + * Returns: @TRUE if commit has been verified successfully, + * @FALSE in case of error or no valid keys are available (@error will contain the reason). + * + * Since: 2020.2 + */ gboolean ostree_sign_commit_verify (OstreeSign *self, OstreeRepo *repo, @@ -254,60 +431,51 @@ ostree_sign_commit_verify (OstreeSign *self, return ostree_sign_data_verify (self, - signed_data, - signatures, - error); + signed_data, + signatures, + error); } -const gchar * ostree_sign_get_name (OstreeSign *self) +/** + * ostree_sign_get_name: + * @self: an #OstreeSign object + * + * Return the pointer to the name of currently used/selected signing engine. + * + * The list of available engines could be acquired with #ostree_sign_list_names. + * + * Returns: (transfer none): pointer to the name + * @NULL in case of error (unlikely). + * + * Since: 2020.2 + */ +const gchar * +ostree_sign_get_name (OstreeSign *self) { g_debug ("%s enter", __FUNCTION__); - g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->get_name != NULL, FALSE); + g_return_val_if_fail (OSTREE_IS_SIGN (self), NULL); + g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->get_name != NULL, NULL); return OSTREE_SIGN_GET_IFACE (self)->get_name (self); } -OstreeSign * ostree_sign_get_by_name (const gchar *name, GError **error) -{ - g_debug ("%s enter", __FUNCTION__); - - OstreeSign *sign = NULL; - - /* Get types if not initialized yet */ -#if defined(HAVE_LIBSODIUM) - if (sign_types[SIGN_ED25519].type == 0) - sign_types[SIGN_ED25519].type = OSTREE_TYPE_SIGN_ED25519; -#endif - if (sign_types[SIGN_DUMMY].type == 0) - sign_types[SIGN_DUMMY].type = OSTREE_TYPE_SIGN_DUMMY; - - for (gint i=0; i < G_N_ELEMENTS(sign_types); i++) - { - if (g_strcmp0 (name, sign_types[i].name) == 0) - { - g_debug ("Found '%s' signing module", sign_types[i].name); - sign = g_object_new (sign_types[i].type, NULL); - break; - } - } - - if (sign == NULL) - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Requested signature type is not implemented"); - - return sign; -} - - /** * ostree_sign_commit: - * @self: Self + * @self: an #OstreeSign object + * @repo: an #OsreeRepo object * @commit_checksum: SHA256 of given commit to sign * @cancellable: A #GCancellable * @error: a #GError * - * Add a GPG signature to a commit. + * Add a signature to a commit. + * + * Depending of the signing engine used you will need to load + * the secret key with #ostree_sign_set_sk. + * + * Returns: @TRUE if commit has been signed successfully, + * @FALSE in case of error (@error will contain the reason). + * + * Since: 2020.2 */ gboolean ostree_sign_commit (OstreeSign *self, @@ -354,7 +522,17 @@ ostree_sign_commit (OstreeSign *self, return TRUE; } -GStrv ostree_sign_list_names(void) +/** + * ostree_sign_list_names: + * + * Return an array with all available sign engines names. + * + * Returns: (transfer full): an array of strings, free when you used it + * + * Since: 2020.2 + */ +GStrv +ostree_sign_list_names(void) { g_debug ("%s enter", __FUNCTION__); @@ -364,8 +542,53 @@ GStrv ostree_sign_list_names(void) for (i=0; i < G_N_ELEMENTS(sign_types); i++) { names[i] = g_strdup(sign_types[i].name); - g_debug ("Found '%s' signing module", names[i]); + g_debug ("Found '%s' signing engine", names[i]); } return names; } + +/** + * ostree_sign_get_by_name: + * @name: the name of desired signature engine + * @error: return location for a #GError + * + * Tries to find and return proper signing engine by it's name. + * + * The list of available engines could be acquired with #ostree_sign_list_names. + * + * Returns: (transfer full): a constant, free when you used it + * + * Since: 2020.2 + */ +OstreeSign * +ostree_sign_get_by_name (const gchar *name, GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + OstreeSign *sign = NULL; + + /* Get types if not initialized yet */ +#if defined(HAVE_LIBSODIUM) + if (sign_types[SIGN_ED25519].type == 0) + sign_types[SIGN_ED25519].type = OSTREE_TYPE_SIGN_ED25519; +#endif + if (sign_types[SIGN_DUMMY].type == 0) + sign_types[SIGN_DUMMY].type = OSTREE_TYPE_SIGN_DUMMY; + + for (gint i=0; i < G_N_ELEMENTS(sign_types); i++) + { + if (g_strcmp0 (name, sign_types[i].name) == 0) + { + g_debug ("Using '%s' signing engine", sign_types[i].name); + sign = g_object_new (sign_types[i].type, NULL); + break; + } + } + + if (sign == NULL) + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Requested signature type is not implemented"); + + return sign; +} diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h index 9add0450..87ed25ce 100644 --- a/src/libostree/ostree-sign.h +++ b/src/libostree/ostree-sign.h @@ -55,7 +55,7 @@ struct _OstreeSignInterface GError **error); gboolean (* data_verify) (OstreeSign *self, GBytes *data, - GVariant *metadata, + GVariant *signatures, GError **error); const gchar *(* metadata_key) (OstreeSign *self); const gchar *(* metadata_format) (OstreeSign *self); @@ -80,16 +80,16 @@ const gchar * ostree_sign_get_name (OstreeSign *self); _OSTREE_PUBLIC gboolean ostree_sign_data (OstreeSign *self, - GBytes *data, - GBytes **signature, - GCancellable *cancellable, - GError **error); + GBytes *data, + GBytes **signature, + GCancellable *cancellable, + GError **error); _OSTREE_PUBLIC gboolean ostree_sign_data_verify (OstreeSign *self, - GBytes *data, - GVariant *signatures, - GError **error); + GBytes *data, + GVariant *signatures, + GError **error); _OSTREE_PUBLIC const gchar * ostree_sign_metadata_key (OstreeSign *self); @@ -136,23 +136,9 @@ gboolean ostree_sign_load_pk (OstreeSign *self, GError **error); -/** - * ostree_sign_list_names: - * - * Return the array with all available sign modules names. - * - * Returns: (transfer full): an array of strings, free when you used it - */ _OSTREE_PUBLIC GStrv ostree_sign_list_names(void); -/** - * ostree_sign_get_by_name: - * - * Tries to find and return proper signing engine by it's name. - * - * Returns: (transfer full): a constant, free when you used it - */ _OSTREE_PUBLIC OstreeSign * ostree_sign_get_by_name (const gchar *name, GError **error); From e799186658e868f1956942ab3d88fd54fd5d441e Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 10 Nov 2019 19:17:58 +0300 Subject: [PATCH 36/75] man: document commit signing Added options descriptions for `ostree-commit` allowing to sign the commit. Signed-off-by: Denis Pynkin --- man/ostree-commit.xml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/man/ostree-commit.xml b/man/ostree-commit.xml index c64a7a00..2c821fc1 100644 --- a/man/ostree-commit.xml +++ b/man/ostree-commit.xml @@ -251,6 +251,39 @@ Boston, MA 02111-1307, USA. POLICY is a boolean which specifies whether fsync should be used or not. Default to true. + + + + + Use particular signature engine. Currently + available ed25519 and dummy + signature types. + + The default is ed25519. + + + + + ="KEY-ID" + + There KEY-ID is: + + + + + base64-encoded secret key for commit signing. + + + + + + + ASCII-string used as secret key. + + + + + From 2fd94388b129724b1bc78fcd280a9680cb16ca01 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 17 Nov 2019 19:58:29 +0300 Subject: [PATCH 37/75] bin/pull-local: add --sign-verify Add option for enabling verification while pulling from local. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-pull-local.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ostree/ot-builtin-pull-local.c b/src/ostree/ot-builtin-pull-local.c index 4b3224f3..4dbd3bfd 100644 --- a/src/ostree/ot-builtin-pull-local.c +++ b/src/ostree/ot-builtin-pull-local.c @@ -39,6 +39,7 @@ static gboolean opt_bareuseronly_files; static gboolean opt_require_static_deltas; static gboolean opt_gpg_verify; static gboolean opt_gpg_verify_summary; +static gboolean opt_sign_verify; static int opt_depth = 0; /* ATTENTION: @@ -55,6 +56,7 @@ static GOptionEntry options[] = { { "require-static-deltas", 0, 0, G_OPTION_ARG_NONE, &opt_require_static_deltas, "Require static deltas", NULL }, { "gpg-verify", 0, 0, G_OPTION_ARG_NONE, &opt_gpg_verify, "GPG verify commits (must specify --remote)", NULL }, { "gpg-verify-summary", 0, 0, G_OPTION_ARG_NONE, &opt_gpg_verify_summary, "GPG verify summary (must specify --remote)", NULL }, + { "sign-verify", 0, 0, G_OPTION_ARG_NONE, &opt_sign_verify, "Verify commits signature (must specify --remote)", NULL }, { "depth", 0, 0, G_OPTION_ARG_INT, &opt_depth, "Traverse DEPTH parents (-1=infinite) (default: 0)", "DEPTH" }, { NULL } }; @@ -182,6 +184,10 @@ ostree_builtin_pull_local (int argc, char **argv, OstreeCommandInvocation *invoc g_variant_builder_add (&builder, "{s@v}", "depth", g_variant_new_variant (g_variant_new_int32 (opt_depth))); + if (opt_sign_verify) + g_variant_builder_add (&builder, "{s@v}", "sign-verify", + g_variant_new_variant (g_variant_new_boolean (TRUE))); + if (console.is_tty) progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console); else From a9df634c474033b29640f57454d1b73fc5f1a070 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 17 Nov 2019 20:02:13 +0300 Subject: [PATCH 38/75] tests/libtest: add functions for ed25519 tests Add functions for keys generation to be used in signing-related tests: - gen_ed25519_keys initializing variables ED25519PUBLIC, ED25519SEED and ED25519SECRET with appropriate base64-encoded keys - gen_ed25519_random_public print a random base64 public key (used in tests with wrong keys) Signed-off-by: Denis Pynkin --- tests/libtest.sh | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/tests/libtest.sh b/tests/libtest.sh index 58a9fd9b..c473fd82 100755 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -673,6 +673,12 @@ which_gpg () { echo ${gpg} } +libtest_cleanup_gpg () { + local gpg_homedir=${1:-${test_tmpdir}/gpghome} + gpg-connect-agent --homedir "${gpg_homedir}" killagent /bye || true +} +libtest_exit_cmds+=(libtest_cleanup_gpg) + has_libsodium () { local ret ${CMD_PREFIX} ostree --version > version.txt @@ -682,12 +688,33 @@ has_libsodium () { return ${ret} } +# Keys for ed25519 signing tests +ED25519PUBLIC= +ED25519SEED= +ED25519SECRET= -libtest_cleanup_gpg () { - local gpg_homedir=${1:-${test_tmpdir}/gpghome} - gpg-connect-agent --homedir "${gpg_homedir}" killagent /bye || true +gen_ed25519_keys () +{ + # Generate private key in PEM format + pemfile="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.pem)" + openssl genpkey -algorithm ed25519 -outform PEM -out "${pemfile}" + + # Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html + # Extract the private and public parts from generated key. + ED25519PUBLIC="$(openssl pkey -outform DER -pubout -in ${pemfile} | tail -c 32 | base64)" + ED25519SEED="$(openssl pkey -outform DER -in ${pemfile} | tail -c 32 | base64)" + # Secret key is concantination of SEED and PUBLIC + ED25519SECRET="$(echo ${ED25519SEED}${ED25519PUBLIC} | base64 -d | base64 -w 0)" + + echo "Generated ed25519 keys:" + echo "public: ${ED25519PUBLIC}" + echo " seed: ${ED25519SEED}" +} + +gen_ed25519_random_public() +{ + openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 } -libtest_exit_cmds+=(libtest_cleanup_gpg) is_bare_user_only_repo () { grep -q 'mode=bare-user-only' $1/config From e474033ea99a89be4ef1984435794409509fdbf7 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 17 Nov 2019 20:15:46 +0300 Subject: [PATCH 39/75] tests/sign: use library functions for ed25519 keys Switch to library functions usage. Signed-off-by: Denis Pynkin --- tests/test-signed-commit.sh | 28 +++++++++++----------------- tests/test-signed-pull.sh | 19 +++++-------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/tests/test-signed-commit.sh b/tests/test-signed-commit.sh index c523aedd..6730a6df 100755 --- a/tests/test-signed-commit.sh +++ b/tests/test-signed-commit.sh @@ -64,18 +64,12 @@ if ! has_libsodium; then fi # Test ostree sign with 'ed25519' module -# Generate private key in PEM format -PEMFILE="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.pem)" -openssl genpkey -algorithm ed25519 -outform PEM -out "${PEMFILE}" +gen_ed25519_keys +PUBLIC=${ED25519PUBLIC} +SEED=${ED25519SEED} +SECRET=${ED25519SECRET} -# Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html -# Extract the private and public parts from generated key. -PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | tail -c 32 | base64)" -SEED="$(openssl pkey -outform DER -in ${PEMFILE} | tail -c 32 | base64)" -# Secret key is concantination of SEED and PUBLIC -SECRET="$(echo ${SEED}${PUBLIC} | base64 -d | base64 -w 0)" - -WRONG_PUBLIC="$(openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64)" +WRONG_PUBLIC="$(gen_ed25519_random_public)" echo "SEED = $SEED" echo "PUBLIC = $PUBLIC" @@ -94,10 +88,10 @@ if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed fi ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} ${PUBLIC} -${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${WRONG_PUBLIC} ${PUBLIC} -${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${WRONG_PUBLIC} ${WRONG_PUBLIC} ${PUBLIC} -${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} ${WRONG_PUBLIC} ${WRONG_PUBLIC} -${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${WRONG_PUBLIC} ${WRONG_PUBLIC} ${PUBLIC} ${WRONG_PUBLIC} ${WRONG_PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} $(gen_ed25519_random_public) ${PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} $(gen_ed25519_random_public) $(gen_ed25519_random_public) ${PUBLIC} +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} ${PUBLIC} $(gen_ed25519_random_public) $(gen_ed25519_random_public) +${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 ${COMMIT} $(gen_ed25519_random_public) $(gen_ed25519_random_public) ${PUBLIC} $(gen_ed25519_random_public) $(gen_ed25519_random_public) echo "ok ed25519 signature verified" # Check if we able to use all available modules to sign the same commit @@ -140,7 +134,7 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed2551 # Test the file with multiple keys without a valid public key for((i=0;i<100;i++)); do # Generate a list with some public signatures - openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 + gen_ed25519_random_public done > ${PUBKEYS} # Check if file contain no valid signatures if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-file=${PUBKEYS} ${COMMIT}; then @@ -177,7 +171,7 @@ echo "ok sign with ed25519 keys file" mkdir -p ${test_tmpdir}/{trusted,revoked}.ed25519.d for((i=0;i<100;i++)); do # Generate some key files with random public signatures - openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 > ${test_tmpdir}/trusted.ed25519.d/signature_$i + gen_ed25519_random_public done # Check no valid public keys are available if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed25519 --keys-dir=${test_tmpdir} ${COMMIT}; then diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh index dc922e81..238ce8e0 100755 --- a/tests/test-signed-pull.sh +++ b/tests/test-signed-pull.sh @@ -70,19 +70,10 @@ test_signed_pull "dummy" # Test ostree sign with 'ed25519' module -# Generate private key in PEM format -PEMFILE="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.pem)" -openssl genpkey -algorithm ed25519 -outform PEM -out "${PEMFILE}" - -# Based on: http://openssl.6102.n7.nabble.com/ed25519-key-generation-td73907.html -# Extract the private and public parts from generated key. -PUBLIC="$(openssl pkey -outform DER -pubout -in ${PEMFILE} | tail -c 32 | base64)" -SEED="$(openssl pkey -outform DER -in ${PEMFILE} | tail -c 32 | base64)" -# Secret key is concantination of SEED and PUBLIC -SECRET="$(echo ${SEED}${PUBLIC} | base64 -d | base64 -w 0)" - -echo "SEED = $SEED" -echo "PUBLIC = $PUBLIC" +gen_ed25519_keys +PUBLIC=${ED25519PUBLIC} +SEED=${ED25519SEED} +SECRET=${ED25519SECRET} COMMIT_ARGS="--sign=${SECRET} --sign-type=ed25519" @@ -96,7 +87,7 @@ PUBKEYS="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.ed25519)" # Test the file with multiple keys without a valid public key for((i=0;i<100;i++)); do # Generate a list with some public signatures - openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64 + gen_ed25519_random_public done > ${PUBKEYS} # Add correct key into the list echo ${PUBLIC} >> ${PUBKEYS} From 72d81d7401919c96ad11612a7395442013fe4394 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 17 Nov 2019 20:17:27 +0300 Subject: [PATCH 40/75] tests/local-pull: test "--sign-verify" option Ensure what with this option only signed commit is pulled. Signed-off-by: Denis Pynkin --- tests/test-local-pull.sh | 119 +++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 43 deletions(-) diff --git a/tests/test-local-pull.sh b/tests/test-local-pull.sh index 97bb9954..2b7ca13a 100755 --- a/tests/test-local-pull.sh +++ b/tests/test-local-pull.sh @@ -28,12 +28,7 @@ unset OSTREE_GPG_HOME skip_without_user_xattrs -if has_gpgme; then - echo "1..8" -else - # Only some tests doesn't need GPG support - echo "1..5" -fi +echo "1..11" setup_test_repository "archive" echo "ok setup" @@ -68,6 +63,49 @@ cmp checkout1.files checkout2.files cmp checkout1.files checkout3.files echo "ok checkouts same" +if has_gpgme; then + # These tests are needed GPG support + mkdir repo4 + ostree_repo_init repo4 --mode="archive" + ${CMD_PREFIX} ostree --repo=repo4 remote add --gpg-import ${test_tmpdir}/gpghome/key1.asc origin repo + + if ${CMD_PREFIX} ostree --repo=repo4 pull-local --remote=origin --gpg-verify repo test2 2>&1; then + assert_not_reached "GPG verification unexpectedly succeeded" + fi + echo "ok --gpg-verify with no signature" + + ${OSTREE} gpg-sign --gpg-homedir=${TEST_GPG_KEYHOME} test2 ${TEST_GPG_KEYID_1} + + mkdir repo5 + ostree_repo_init repo5 --mode="archive" + ${CMD_PREFIX} ostree --repo=repo5 remote add --gpg-import ${test_tmpdir}/gpghome/key1.asc origin repo + ${CMD_PREFIX} ostree --repo=repo5 pull-local --remote=origin --gpg-verify repo test2 + echo "ok --gpg-verify" + + mkdir repo6 + ostree_repo_init repo6 --mode="archive" + ${CMD_PREFIX} ostree --repo=repo6 remote add --gpg-import ${test_tmpdir}/gpghome/key1.asc origin repo + if ${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1; then + assert_not_reached "GPG summary verification with no summary unexpectedly succeeded" + fi + + ${OSTREE} summary --update + + if ${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1; then + assert_not_reached "GPG summary verification with signed no summary unexpectedly succeeded" + fi + + ${OSTREE} summary --update --gpg-sign=${TEST_GPG_KEYID_1} --gpg-homedir=${TEST_GPG_KEYHOME} + + ${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1 + + echo "ok --gpg-verify-summary" +else + echo "ok --gpg-verify with no signature | # SKIP due GPG unavailability" + echo "ok --gpg-verify | # SKIP due GPG unavailability" + echo "ok --gpg-verify-summary | # SKIP due GPG unavailability" +fi + mkdir repo7 ostree_repo_init repo7 --mode="archive" ${CMD_PREFIX} ostree --repo=repo7 pull-local repo @@ -78,41 +116,36 @@ for src_object in `find repo/objects -name '*.filez'`; do done echo "ok pull-local z2 to z2 default hardlink" -if ! has_gpgme; then - exit 0 +if has_libsodium; then + gen_ed25519_keys + + mkdir repo8 + ostree_repo_init repo8 --mode="archive" + ${CMD_PREFIX} ostree --repo=repo8 remote add --set=verification-key="${ED25519PUBLIC}" origin repo + cat repo8/config + + if ${CMD_PREFIX} ostree --repo=repo8 pull-local --remote=origin --sign-verify repo test2 2>&1; then + assert_not_reached "Ed25519 signature verification unexpectedly succeeded" + fi + echo "ok --sign-verify with no signature" + + ${OSTREE} sign test2 ${ED25519SECRET} + + mkdir repo9 + ostree_repo_init repo9 --mode="archive" + ${CMD_PREFIX} ostree --repo=repo9 remote add --set=verification-key="$(gen_ed25519_random_public)" origin repo + if ${CMD_PREFIX} ostree --repo=repo9 pull-local --remote=origin --sign-verify repo test2 2>&1; then + assert_not_reached "Ed25519 signature verification unexpectedly succeeded" + fi + echo "ok --sign-verify with wrong signature" + + mkdir repo10 + ostree_repo_init repo10 --mode="archive" + ${CMD_PREFIX} ostree --repo=repo10 remote add --set=verification-key="${ED25519PUBLIC}" origin repo + ${CMD_PREFIX} ostree --repo=repo10 pull-local --remote=origin --sign-verify repo test2 + echo "ok --sign-verify" +else + echo "ok --sign-verify with no signature | # SKIP due libsodium unavailability" + echo "ok --sign-verify with wrong signature | # SKIP due libsodium unavailability" + echo "ok --sign-verify | # SKIP libsodium unavailability" fi - -mkdir repo4 -ostree_repo_init repo4 --mode="archive" -${CMD_PREFIX} ostree --repo=repo4 remote add --gpg-import ${test_tmpdir}/gpghome/key1.asc origin repo -if ${CMD_PREFIX} ostree --repo=repo4 pull-local --remote=origin --gpg-verify repo test2 2>&1; then - assert_not_reached "GPG verification unexpectedly succeeded" -fi -echo "ok --gpg-verify with no signature" - -${OSTREE} gpg-sign --gpg-homedir=${TEST_GPG_KEYHOME} test2 ${TEST_GPG_KEYID_1} - -mkdir repo5 -ostree_repo_init repo5 --mode="archive" -${CMD_PREFIX} ostree --repo=repo5 remote add --gpg-import ${test_tmpdir}/gpghome/key1.asc origin repo -${CMD_PREFIX} ostree --repo=repo5 pull-local --remote=origin --gpg-verify repo test2 -echo "ok --gpg-verify" - -mkdir repo6 -ostree_repo_init repo6 --mode="archive" -${CMD_PREFIX} ostree --repo=repo6 remote add --gpg-import ${test_tmpdir}/gpghome/key1.asc origin repo -if ${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1; then - assert_not_reached "GPG summary verification with no summary unexpectedly succeeded" -fi - -${OSTREE} summary --update - -if ${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1; then - assert_not_reached "GPG summary verification with signed no summary unexpectedly succeeded" -fi - -${OSTREE} summary --update --gpg-sign=${TEST_GPG_KEYID_1} --gpg-homedir=${TEST_GPG_KEYHOME} - -${CMD_PREFIX} ostree --repo=repo6 pull-local --remote=origin --gpg-verify-summary repo test2 2>&1 - -echo "ok --gpg-verify-summary" From 6608436441d390f4b0b9de2bef33503921daa13c Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 17 Nov 2019 20:58:33 +0300 Subject: [PATCH 41/75] bin/remote-add: added "--no-sign-verify" option Option "--no-sign-verify" disable the signature verification while adding remote. Signed-off-by: Denis Pynkin --- src/ostree/ot-remote-builtin-add.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ostree/ot-remote-builtin-add.c b/src/ostree/ot-remote-builtin-add.c index cea0b274..e4634710 100644 --- a/src/ostree/ot-remote-builtin-add.c +++ b/src/ostree/ot-remote-builtin-add.c @@ -28,6 +28,7 @@ static char **opt_set; static gboolean opt_no_gpg_verify; +static gboolean opt_no_sign_verify; static gboolean opt_if_not_exists; static gboolean opt_force; static char *opt_gpg_import; @@ -44,6 +45,7 @@ static char *opt_repo; static GOptionEntry option_entries[] = { { "set", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_set, "Set config option KEY=VALUE for remote", "KEY=VALUE" }, { "no-gpg-verify", 0, 0, G_OPTION_ARG_NONE, &opt_no_gpg_verify, "Disable GPG verification", NULL }, + { "no-sign-verify", 0, 0, G_OPTION_ARG_NONE, &opt_no_sign_verify, "Disable signature verification", NULL }, { "if-not-exists", 0, 0, G_OPTION_ARG_NONE, &opt_if_not_exists, "Do nothing if the provided remote exists", NULL }, { "force", 0, 0, G_OPTION_ARG_NONE, &opt_force, "Replace the provided remote if it exists", NULL }, { "gpg-import", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_import, "Import GPG key from FILE", "FILE" }, @@ -134,12 +136,18 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio } #ifndef OSTREE_DISABLE_GPGME - if (opt_no_gpg_verify) + /* No signature verification implies no verification for GPG signature as well */ + if (opt_no_gpg_verify || opt_no_sign_verify) g_variant_builder_add (optbuilder, "{s@v}", "gpg-verify", g_variant_new_variant (g_variant_new_boolean (FALSE))); #endif /* OSTREE_DISABLE_GPGME */ + if (opt_no_sign_verify) + g_variant_builder_add (optbuilder, "{s@v}", + "sign-verify", + g_variant_new_variant (g_variant_new_boolean (FALSE))); + if (opt_collection_id != NULL) g_variant_builder_add (optbuilder, "{s@v}", "collection-id", g_variant_new_variant (g_variant_new_take_string (g_steal_pointer (&opt_collection_id)))); From 68aa13550a403eb30e0e0a759198a005fb631445 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sun, 17 Nov 2019 21:01:13 +0300 Subject: [PATCH 42/75] tests: use option "--no-sign-verify" for adding remote Option "--no-sign-verify" disable the signature verification including GPG. So use it in tests instead of "--no-gpg-verification". Signed-off-by: Denis Pynkin --- tests/pull-test.sh | 32 +++++++++++++++---------------- tests/pull-test2.sh | 4 ++-- tests/test-pull-mirrorlist.sh | 6 +++--- tests/test-refs-collections.sh | 4 ++-- tests/test-remote-add.sh | 10 +++++----- tests/test-summary-collections.sh | 4 ++-- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/pull-test.sh b/tests/pull-test.sh index 2cfd8e02..a52adab9 100644 --- a/tests/pull-test.sh +++ b/tests/pull-test.sh @@ -29,7 +29,7 @@ function repo_init() { ${CMD_PREFIX} ostree --repo=repo remote add origin $(cat httpd-address)/ostree/gnomerepo "$@" } -repo_init --no-gpg-verify +repo_init --no-sign-verify # See also the copy of this in basic-test.sh COMMIT_ARGS="" @@ -62,7 +62,7 @@ else fi # Try both syntaxes -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main >out.txt assert_file_has_content out.txt "[1-9][0-9]* metadata, [1-9][0-9]* content objects fetched" ${CMD_PREFIX} ostree --repo=repo pull origin:main > out.txt @@ -164,7 +164,7 @@ echo "ok pull (bareuseronly mirror)" # Corruption tests cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify if ! is_bare_user_only_repo repo; then if ! skip_one_without_user_xattrs; then if is_bare_user_only_repo repo; then @@ -216,7 +216,7 @@ if ! skip_one_without_user_xattrs; then done # And ensure the repo is reinitialized - repo_init --no-gpg-verify + repo_init --no-sign-verify echo "ok corruption" fi else @@ -320,7 +320,7 @@ echo "ok pull specific commit" # test pull -T cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main origrev=$(${CMD_PREFIX} ostree --repo=repo rev-parse main) # Check we can pull the same commit with timestamp checking enabled @@ -350,7 +350,7 @@ ${CMD_PREFIX} ostree --repo=repo pull origin main echo "ok pull timestamp checking" cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main ${CMD_PREFIX} ostree --repo=repo fsck # Generate a delta from old to current, even though we aren't going to @@ -375,7 +375,7 @@ ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u # Explicitly test delta fetches via ref name as well as commit hash for delta_target in main ${new_rev}; do cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main@${prev_rev} ${CMD_PREFIX} ostree --repo=repo pull --dry-run --require-static-deltas origin ${delta_target} >dry-run-pull.txt # Compression can vary, so we support 400-699 @@ -388,7 +388,7 @@ done # Test pull via file:/// - this should still use the deltas path for testing cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo remote delete origin ${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin file://$(pwd)/ostree-srv/gnomerepo ${CMD_PREFIX} ostree --repo=repo pull origin main@${prev_rev} @@ -400,7 +400,7 @@ echo "ok pull file:// + deltas required" # Explicitly test delta fetches via ref name as well as commit hash for delta_target in main ${new_rev}; do cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main@${prev_rev} ${CMD_PREFIX} ostree --repo=repo pull --require-static-deltas origin ${delta_target} if test ${delta_target} = main; then @@ -414,12 +414,12 @@ done # Test no-op with deltas: https://github.com/ostreedev/ostree/issues/1321 cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main ${CMD_PREFIX} ostree --repo=repo pull --require-static-deltas origin main cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main@${prev_rev} ${CMD_PREFIX} ostree --repo=repo pull --disable-static-deltas origin main ${CMD_PREFIX} ostree --repo=repo fsck @@ -437,7 +437,7 @@ cd ${test_tmpdir} ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo static-delta generate --swap-endianness main ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main@${prev_rev} ${CMD_PREFIX} ostree --repo=repo pull --require-static-deltas --dry-run origin main >byteswapped-dry-run-pull.txt ${CMD_PREFIX} ostree --repo=repo fsck @@ -451,7 +451,7 @@ echo "ok pull byteswapped delta" cd ${test_tmpdir} rm ostree-srv/gnomerepo/deltas -rf ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u -repo_init --no-gpg-verify +repo_init --no-sign-verify if ${CMD_PREFIX} ostree --repo=repo pull --require-static-deltas origin main 2>err.txt; then assert_not_reached "--require-static-deltas unexpectedly succeeded" fi @@ -459,7 +459,7 @@ assert_file_has_content err.txt "deltas required, but none found" ${CMD_PREFIX} ostree --repo=repo fsck # Now test with a partial commit -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull --commit-metadata-only origin main@${prev_rev} if ${CMD_PREFIX} ostree --repo=repo pull --require-static-deltas origin main 2>err.txt; then assert_not_reached "--require-static-deltas unexpectedly succeeded" @@ -467,7 +467,7 @@ fi assert_file_has_content err.txt "deltas required, but none found" echo "ok delta required but don't exist" -repo_init --no-gpg-verify +repo_init --no-sign-verify ${CMD_PREFIX} ostree --repo=repo pull origin main@${prev_rev} if ${CMD_PREFIX} ostree --repo=repo pull --require-static-deltas origin ${new_rev} 2>err.txt; then assert_not_reached "--require-static-deltas unexpectedly succeeded" @@ -595,7 +595,7 @@ if has_gpgme; then fi cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify mv ostree-srv/gnomerepo/refs/heads/main{,.orig} rm ostree-srv/gnomerepo/summary (for x in $(seq 20); do echo "lots of html here "; done) > ostree-srv/gnomerepo/refs/heads/main diff --git a/tests/pull-test2.sh b/tests/pull-test2.sh index 064bbfe6..a0b699ae 100644 --- a/tests/pull-test2.sh +++ b/tests/pull-test2.sh @@ -29,7 +29,7 @@ function repo_init() { ${CMD_PREFIX} ostree --repo=repo remote add origin $(cat httpd-address)/ostree/gnomerepo "$@" } -repo_init --no-gpg-verify +repo_init --no-sign-verify # See also the copy of this in basic-test.sh COMMIT_ARGS="" @@ -48,7 +48,7 @@ fi echo "1..1" cd ${test_tmpdir} -repo_init --no-gpg-verify +repo_init --no-sign-verify prev_rev=$(ostree --repo=ostree-srv/repo rev-parse ${remote_ref}^) rev=$(ostree --repo=ostree-srv/repo rev-parse ${remote_ref}) ${CMD_PREFIX} ostree --repo=ostree-srv/repo static-delta generate ${remote_ref} diff --git a/tests/test-pull-mirrorlist.sh b/tests/test-pull-mirrorlist.sh index 85ff66e9..ed65eb64 100755 --- a/tests/test-pull-mirrorlist.sh +++ b/tests/test-pull-mirrorlist.sh @@ -75,7 +75,7 @@ EOF cd ${test_tmpdir} mkdir repo ostree_repo_init repo -${CMD_PREFIX} ostree --repo=repo remote add origin --no-gpg-verify \ +${CMD_PREFIX} ostree --repo=repo remote add origin --no-sign-verify \ mirrorlist=$(cat httpd-address)/ostree/mirrorlist ${CMD_PREFIX} ostree --repo=repo pull origin:main @@ -87,7 +87,7 @@ cd ${test_tmpdir} rm -rf repo mkdir repo ostree_repo_init repo -${CMD_PREFIX} ostree --repo=repo remote add origin --no-gpg-verify \ +${CMD_PREFIX} ostree --repo=repo remote add origin --no-sign-verify \ --contenturl=mirrorlist=$(cat httpd-address)/ostree/mirrorlist \ $(cat httpd-address)/ostree/gnomerepo ${CMD_PREFIX} ostree --repo=repo pull origin:main @@ -100,7 +100,7 @@ cd ${test_tmpdir} rm -rf repo mkdir repo ostree_repo_init repo -${CMD_PREFIX} ostree --repo=repo remote add origin --no-gpg-verify \ +${CMD_PREFIX} ostree --repo=repo remote add origin --no-sign-verify \ --contenturl=mirrorlist=$(cat httpd-address)/ostree/mirrorlist \ mirrorlist=$(cat httpd-address)/ostree/mirrorlist ${CMD_PREFIX} ostree --repo=repo pull origin:main diff --git a/tests/test-refs-collections.sh b/tests/test-refs-collections.sh index bf233970..d33f498c 100755 --- a/tests/test-refs-collections.sh +++ b/tests/test-refs-collections.sh @@ -112,7 +112,7 @@ mkdir collection-repo ostree_repo_init collection-repo --collection-id org.example.RemoteCollection mkdir -p adir ${CMD_PREFIX} ostree --repo=collection-repo commit --branch=rcommit -m rcommit -s rcommit adir -${CMD_PREFIX} ostree --repo=repo remote add --no-gpg-verify --collection-id org.example.RemoteCollection collection-repo-remote "file://${test_tmpdir}/collection-repo" +${CMD_PREFIX} ostree --repo=repo remote add --no-sign-verify --collection-id org.example.RemoteCollection collection-repo-remote "file://${test_tmpdir}/collection-repo" ${CMD_PREFIX} ostree --repo=repo pull collection-repo-remote rcommit ${CMD_PREFIX} ostree --repo=repo refs --collections > refs @@ -129,7 +129,7 @@ mkdir no-collection-repo ostree_repo_init no-collection-repo mkdir -p adir2 ${CMD_PREFIX} ostree --repo=no-collection-repo commit --branch=rcommit2 -m rcommit2 -s rcommit2 adir2 -${CMD_PREFIX} ostree --repo=repo remote add --no-gpg-verify no-collection-repo-remote "file://${test_tmpdir}/no-collection-repo" +${CMD_PREFIX} ostree --repo=repo remote add --no-sign-verify no-collection-repo-remote "file://${test_tmpdir}/no-collection-repo" ${CMD_PREFIX} ostree --repo=repo pull no-collection-repo-remote rcommit2 ${CMD_PREFIX} ostree --repo=repo refs --collections > refs assert_not_file_has_content refs "rcommit2" diff --git a/tests/test-remote-add.sh b/tests/test-remote-add.sh index bb7eae89..40a32f57 100755 --- a/tests/test-remote-add.sh +++ b/tests/test-remote-add.sh @@ -30,20 +30,20 @@ $OSTREE remote add origin http://example.com/ostree/gnome $OSTREE remote show-url origin >/dev/null echo "ok config" -$OSTREE remote add --no-gpg-verify another http://another.com/repo +$OSTREE remote add --no-sign-verify another http://another.com/repo $OSTREE remote show-url another >/dev/null echo "ok remote no gpg-verify" -if $OSTREE remote add --no-gpg-verify another http://another.example.com/anotherrepo 2>err.txt; then +if $OSTREE remote add --no-sign-verify another http://another.example.com/anotherrepo 2>err.txt; then assert_not_reached "Adding duplicate remote unexpectedly succeeded" fi echo "ok" -$OSTREE remote add --if-not-exists --no-gpg-verify another http://another.example.com/anotherrepo +$OSTREE remote add --if-not-exists --no-sign-verify another http://another.example.com/anotherrepo $OSTREE remote show-url another >/dev/null echo "ok" -$OSTREE remote add --if-not-exists --no-gpg-verify another-noexist http://another-noexist.example.com/anotherrepo +$OSTREE remote add --if-not-exists --no-sign-verify another-noexist http://another-noexist.example.com/anotherrepo $OSTREE remote show-url another-noexist >/dev/null echo "ok" @@ -69,7 +69,7 @@ cd ${test_tmpdir} rm -rf parent-repo ostree_repo_init parent-repo $OSTREE config set core.parent ${test_tmpdir}/parent-repo -${CMD_PREFIX} ostree --repo=parent-repo remote add --no-gpg-verify parent-remote http://parent-remote.example.com/parent-remote +${CMD_PREFIX} ostree --repo=parent-repo remote add --no-sign-verify parent-remote http://parent-remote.example.com/parent-remote $OSTREE remote list > list.txt assert_file_has_content list.txt "origin" assert_file_has_content list.txt "another" diff --git a/tests/test-summary-collections.sh b/tests/test-summary-collections.sh index 777d3d0c..9885c5ea 100755 --- a/tests/test-summary-collections.sh +++ b/tests/test-summary-collections.sh @@ -63,7 +63,7 @@ mkdir collection-repo ostree_repo_init collection-repo --collection-id org.example.RemoteCollection mkdir -p adir ${CMD_PREFIX} ostree --repo=collection-repo commit --branch=rcommit -m rcommit -s rcommit adir -${CMD_PREFIX} ostree --repo=repo remote add --no-gpg-verify --collection-id org.example.RemoteCollection collection-repo-remote "file://${test_tmpdir}/collection-repo" +${CMD_PREFIX} ostree --repo=repo remote add --no-sign-verify --collection-id org.example.RemoteCollection collection-repo-remote "file://${test_tmpdir}/collection-repo" ${CMD_PREFIX} ostree --repo=repo pull collection-repo-remote rcommit ${CMD_PREFIX} ostree --repo=repo summary --update @@ -75,7 +75,7 @@ mkdir no-collection-repo ostree_repo_init no-collection-repo mkdir -p adir2 ${CMD_PREFIX} ostree --repo=no-collection-repo commit --branch=rcommit2 -m rcommit2 -s rcommit2 adir2 -${CMD_PREFIX} ostree --repo=repo remote add --no-gpg-verify no-collection-repo-remote "file://${test_tmpdir}/no-collection-repo" +${CMD_PREFIX} ostree --repo=repo remote add --no-sign-verify no-collection-repo-remote "file://${test_tmpdir}/no-collection-repo" ${CMD_PREFIX} ostree --repo=repo pull no-collection-repo-remote rcommit2 ${CMD_PREFIX} ostree --repo=repo summary --update From fbd2666e076c1cf4bd0a1b13c888b21094b1f97f Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 18 Nov 2019 14:28:40 +0300 Subject: [PATCH 43/75] tests/sign: disable GPG for alternatively signed pull Explicitly disable GPG verification for remote while testing alternative signing mechanism. Signed-off-by: Denis Pynkin --- tests/test-signed-pull.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh index 238ce8e0..28676b21 100755 --- a/tests/test-signed-pull.sh +++ b/tests/test-signed-pull.sh @@ -34,7 +34,7 @@ function repo_init() { rm repo -rf mkdir repo ostree_repo_init repo --mode=${repo_mode} - ${CMD_PREFIX} ostree --repo=repo remote add origin $(cat httpd-address)/ostree/gnomerepo "$@" + ${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin $(cat httpd-address)/ostree/gnomerepo "$@" } function test_signed_pull() { From 485ff7335faf8afb9b4a47eca71cbae10ccd75aa Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 19 Nov 2019 02:44:16 +0300 Subject: [PATCH 44/75] lib/sign: allow to build with glib version less than 2.44 Ubuntu 14.04 uses glib-2.40 which have no some shiny macroses for interface declaration. Signed-off-by: Denis Pynkin --- src/libostree/ostree-autocleanups.h | 1 + src/libostree/ostree-sign-dummy.c | 5 +++++ src/libostree/ostree-sign-dummy.h | 14 ++++++++++++++ src/libostree/ostree-sign-ed25519.c | 5 +++++ src/libostree/ostree-sign-ed25519.h | 15 ++++++++++++++- src/libostree/ostree-sign.c | 2 ++ src/libostree/ostree-sign.h | 18 ++++++++++++++---- src/libostree/ostree.h | 1 + 8 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/libostree/ostree-autocleanups.h b/src/libostree/ostree-autocleanups.h index c9692ebe..14017012 100644 --- a/src/libostree/ostree-autocleanups.h +++ b/src/libostree/ostree-autocleanups.h @@ -73,6 +73,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderOverride, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderResult, ostree_repo_finder_result_free) G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeRepoFinderResultv, ostree_repo_finder_result_freev, NULL) +G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSign, g_object_unref) #endif G_END_DECLS diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index e2d1fe56..48190149 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -24,6 +24,7 @@ #include "config.h" +#include #include "ostree-sign-dummy.h" #include @@ -43,6 +44,10 @@ struct _OstreeSignDummy gchar *signature_ascii; }; +#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC +G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSignDummy, g_object_unref) +#endif + static void ostree_sign_dummy_iface_init (OstreeSignInterface *self); diff --git a/src/libostree/ostree-sign-dummy.h b/src/libostree/ostree-sign-dummy.h index a0d10e1d..f80f8682 100644 --- a/src/libostree/ostree-sign-dummy.h +++ b/src/libostree/ostree-sign-dummy.h @@ -32,12 +32,26 @@ G_BEGIN_DECLS #define OSTREE_TYPE_SIGN_DUMMY (ostree_sign_dummy_get_type ()) +_OSTREE_PUBLIC +GType ostree_sign_dummy_get_type (void); + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +typedef struct _OstreeSignDummy OstreeSignDummy; +typedef struct { GObjectClass parent_class; } OstreeSignDummyClass; + +static inline OstreeSignDummy *OSTREE_SIGN_DUMMY (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_sign_dummy_get_type (), OstreeSignDummy); } +static inline gboolean OSTREE_IS_SIGN_DUMMY (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_sign_dummy_get_type ()); } + +G_GNUC_END_IGNORE_DEPRECATIONS + +/* Have to use glib-2.44 for this _OSTREE_PUBLIC G_DECLARE_FINAL_TYPE (OstreeSignDummy, ostree_sign_dummy, OSTREE, SIGN_DUMMY, GObject) +*/ const gchar * ostree_sign_dummy_get_name (OstreeSign *self); diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 0c7cd951..2d5bdb16 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -25,6 +25,7 @@ #include "config.h" +#include #include "ostree-sign-ed25519.h" #ifdef HAVE_LIBSODIUM #include @@ -47,6 +48,10 @@ struct _OstreeSignEd25519 GList *revoked_keys; }; +#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC +G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSignEd25519, g_object_unref) +#endif + static void ostree_sign_ed25519_iface_init (OstreeSignInterface *self); diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h index bced1cdf..6e5dd665 100644 --- a/src/libostree/ostree-sign-ed25519.h +++ b/src/libostree/ostree-sign-ed25519.h @@ -32,13 +32,26 @@ G_BEGIN_DECLS #define OSTREE_TYPE_SIGN_ED25519 (ostree_sign_ed25519_get_type ()) +_OSTREE_PUBLIC +GType ostree_sign_ed25519_get_type (void); + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +typedef struct _OstreeSignEd25519 OstreeSignEd25519; +typedef struct { GObjectClass parent_class; } OstreeSignEd25519Class; + +static inline OstreeSignEd25519 *OSTREE_SIGN_ED25519 (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_sign_ed25519_get_type (), OstreeSignEd25519); } +static inline gboolean OSTREE_IS_SIGN_ED25519 (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_sign_ed25519_get_type ()); } + +G_GNUC_END_IGNORE_DEPRECATIONS + +/* Have to use glib-2.44 for this _OSTREE_PUBLIC G_DECLARE_FINAL_TYPE (OstreeSignEd25519, ostree_sign_ed25519, OSTREE, SIGN_ED25519, GObject) - +*/ gboolean ostree_sign_ed25519_data (OstreeSign *self, GBytes *data, diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index 95319f67..75db0b26 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -47,6 +47,8 @@ #include "ostree-sign-ed25519.h" #endif +#include "ostree-autocleanups.h" + #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "OSTreeSign" diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h index 87ed25ce..1415becb 100644 --- a/src/libostree/ostree-sign.h +++ b/src/libostree/ostree-sign.h @@ -33,16 +33,26 @@ #include "ostree-remote.h" #include "ostree-types.h" -/* Special remote */ -#define OSTREE_SIGN_ALL_REMOTES "__OSTREE_ALL_REMOTES__" - - G_BEGIN_DECLS #define OSTREE_TYPE_SIGN (ostree_sign_get_type ()) +_OSTREE_PUBLIC +GType ostree_sign_get_type (void); + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +typedef struct _OstreeSign OstreeSign; +typedef struct _OstreeSignInterface OstreeSignInterface; + +static inline OstreeSign *OSTREE_SIGN (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_sign_get_type (), OstreeSign); } +static inline gboolean OSTREE_IS_SIGN (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_sign_get_type ()); } +static inline OstreeSignInterface *OSTREE_SIGN_GET_IFACE (gpointer ptr) { return G_TYPE_INSTANCE_GET_INTERFACE (ptr, ostree_sign_get_type (), OstreeSignInterface); } +G_GNUC_END_IGNORE_DEPRECATIONS + +/* Have to use glib-2.44 for this _OSTREE_PUBLIC G_DECLARE_INTERFACE (OstreeSign, ostree_sign, OSTREE, SIGN, GObject) +*/ struct _OstreeSignInterface { diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h index 49ca919c..0308d0ed 100644 --- a/src/libostree/ostree.h +++ b/src/libostree/ostree.h @@ -40,5 +40,6 @@ #include #include #include +#include #include #include From 3063a0a838981aa0e04e6f2acb2cc78ce6bb068d Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 25 Nov 2019 22:20:03 +0300 Subject: [PATCH 45/75] lib/sign: use separate public and secret keys for 'dummy' The initial implementation with single key for secret and public parts doesn't allow to test pulling with several signing engines used. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-dummy.c | 39 +++++++++++++++++++++---------- src/libostree/ostree-sign-dummy.h | 4 +++- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index 48190149..034c7d6b 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -36,12 +36,11 @@ #define OSTREE_SIGN_METADATA_DUMMY_KEY "ostree.sign.dummy" #define OSTREE_SIGN_METADATA_DUMMY_TYPE "aay" -#define OSTREE_SIGN_DUMMY_SIGNATURE "dummysign" - struct _OstreeSignDummy { GObject parent; - gchar *signature_ascii; + gchar *sk_ascii; + gchar *pk_ascii; }; #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC @@ -64,8 +63,10 @@ ostree_sign_dummy_iface_init (OstreeSignInterface *self) self->data_verify = ostree_sign_dummy_data_verify; self->metadata_key = ostree_sign_dummy_metadata_key; self->metadata_format = ostree_sign_dummy_metadata_format; - self->set_sk = ostree_sign_dummy_set_key; - self->set_pk = ostree_sign_dummy_set_key; + self->set_sk = ostree_sign_dummy_set_sk; + self->set_pk = ostree_sign_dummy_set_pk; + /* Implementation for dummy engine just load the single public key */ + self->add_pk = ostree_sign_dummy_set_pk; } static void @@ -79,19 +80,32 @@ ostree_sign_dummy_init (OstreeSignDummy *self) { g_debug ("%s enter", __FUNCTION__); - self->signature_ascii = g_strdup(OSTREE_SIGN_DUMMY_SIGNATURE); + self->sk_ascii = NULL; + self->pk_ascii = NULL; } -gboolean ostree_sign_dummy_set_key (OstreeSign *self, GVariant *key, GError **error) +gboolean ostree_sign_dummy_set_sk (OstreeSign *self, GVariant *key, GError **error) { g_debug ("%s enter", __FUNCTION__); OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); - if (sign->signature_ascii != NULL) - g_free(sign->signature_ascii); + g_free(sign->sk_ascii); - sign->signature_ascii = g_variant_dup_string (key, 0); + sign->sk_ascii = g_variant_dup_string (key, 0); + + return TRUE; +} + +gboolean ostree_sign_dummy_set_pk (OstreeSign *self, GVariant *key, GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + + OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + + g_free(sign->pk_ascii); + + sign->pk_ascii = g_variant_dup_string (key, 0); return TRUE; } @@ -108,7 +122,7 @@ gboolean ostree_sign_dummy_data (OstreeSign *self, OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); - *signature = g_bytes_new (sign->signature_ascii, strlen(sign->signature_ascii)); + *signature = g_bytes_new (sign->sk_ascii, strlen(sign->sk_ascii)); return TRUE; } @@ -174,8 +188,9 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, g_bytes_get_data (signature, &sign_size); g_autofree gchar *sign_ascii = g_strndup(g_bytes_get_data (signature, NULL), sign_size); g_debug("Read signature %d: %s", (gint)i, sign_ascii); + g_debug("Stored signature %d: %s", (gint)i, sign->pk_ascii); - if (!g_strcmp0(sign_ascii, sign->signature_ascii)) + if (!g_strcmp0(sign_ascii, sign->pk_ascii)) ret = TRUE; } if (ret == FALSE && *error == NULL) diff --git a/src/libostree/ostree-sign-dummy.h b/src/libostree/ostree-sign-dummy.h index f80f8682..6fc3a363 100644 --- a/src/libostree/ostree-sign-dummy.h +++ b/src/libostree/ostree-sign-dummy.h @@ -69,7 +69,9 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, const gchar * ostree_sign_dummy_metadata_key (OstreeSign *self); const gchar * ostree_sign_dummy_metadata_format (OstreeSign *self); -gboolean ostree_sign_dummy_set_key (OstreeSign *self, GVariant *key, GError **error); +gboolean ostree_sign_dummy_set_sk (OstreeSign *self, GVariant *key, GError **error); +gboolean ostree_sign_dummy_set_pk (OstreeSign *self, GVariant *key, GError **error); +gboolean ostree_sign_dummy_add_pk (OstreeSign *self, GVariant *key, GError **error); G_END_DECLS From 5dca74fab7a5419e4151de4b13de8a351b8b724b Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 25 Nov 2019 19:50:07 +0000 Subject: [PATCH 46/75] tests/sign: add verification key for pulling with dummy After splitting out the common key to secret/public inside the dummy engine we need to pass the the public key for remote with dummy engine usage. Signed-off-by: Denis Pynkin --- tests/test-signed-pull.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh index 28676b21..e57a40f3 100755 --- a/tests/test-signed-pull.sh +++ b/tests/test-signed-pull.sh @@ -34,7 +34,7 @@ function repo_init() { rm repo -rf mkdir repo ostree_repo_init repo --mode=${repo_mode} - ${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin $(cat httpd-address)/ostree/gnomerepo "$@" + ${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false --set=sign-verify-summary=false origin $(cat httpd-address)/ostree/gnomerepo "$@" } function test_signed_pull() { @@ -66,6 +66,7 @@ function test_signed_pull() { DUMMYSIGN="dummysign" COMMIT_ARGS="--sign=${DUMMYSIGN} --sign-type=dummy" repo_init --set=sign-verify=true +${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-key "${DUMMYSIGN}" test_signed_pull "dummy" From 5cd822ae0528ac3b573e7d01c1a01f47f4722396 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 25 Nov 2019 22:53:28 +0300 Subject: [PATCH 47/75] lib/sign: fix the false failure while loading keys Usage of 'g_warning()' inside keys loading funcrion lead to false failure: the key loading attempt for the wrong engine breaks the pulling process instead of trying to use this key with correct engine. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 4dd0f11a..78bb32f3 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1492,7 +1492,6 @@ _load_public_keys (OtPullData *pull_data, gboolean loaded_inlined = TRUE; g_autoptr (GError) error = NULL; - /* Load keys for remote from file */ ostree_repo_get_remote_option (pull_data->repo, pull_data->remote_name, "verification-file", NULL, @@ -1531,12 +1530,9 @@ _load_public_keys (OtPullData *pull_data, loaded_from_file = TRUE; else { - if (error == NULL) - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, - "unknown reason"); - - g_warning("Unable to load public keys from file '%s': %s", - pk_file, error->message); + g_assert (error); + g_debug("Unable to load public keys for '%s' from file '%s': %s", + ostree_sign_get_name(sign), pk_file, error->message); g_clear_error (&error); } } @@ -1557,8 +1553,8 @@ _load_public_keys (OtPullData *pull_data, g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "unknown reason"); - g_warning("Unable to load public key '%s': %s", - pk_ascii, error->message); + g_debug("Unable to load public key '%s' for '%s': %s", + pk_ascii, ostree_sign_get_name(sign), error->message); g_clear_error (&error); } } From acace9b95ab63b64f7d0bc349d94372ccd1a9575 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 26 Nov 2019 09:40:57 +0000 Subject: [PATCH 48/75] tests/sign: allow to start pull test without libsodium Allow to run the pulling test if there is no ed25519 support. Test the signed pull only with dummy engine. Fixed tests names. Signed-off-by: Denis Pynkin --- tests/test-signed-pull.sh | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh index e57a40f3..4d8d7aab 100755 --- a/tests/test-signed-pull.sh +++ b/tests/test-signed-pull.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..7" +echo "1..6" setup_fake_remote_repo1 "archive" @@ -39,6 +39,7 @@ function repo_init() { function test_signed_pull() { local sign_type="$1" + local comment="$2" cd ${test_tmpdir} ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo commit ${COMMIT_ARGS} \ -b main -s "A signed commit" --tree=ref=main @@ -56,19 +57,26 @@ function test_signed_pull() { # ok now check that we can pull correctly mv $remotesig.bak $remotesig ${CMD_PREFIX} ostree --repo=repo pull origin main - echo "ok pull ${sign_type} signed commit" + echo "ok ${sign_type}${comment} pull signed commit" rm $localsig ${CMD_PREFIX} ostree --repo=repo pull origin main test -f $localsig - echo "ok re-pull ${sign_type} signature for stored commit" + echo "ok ${sign_type}${comment} re-pull signature for stored commit" } DUMMYSIGN="dummysign" COMMIT_ARGS="--sign=${DUMMYSIGN} --sign-type=dummy" repo_init --set=sign-verify=true ${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-key "${DUMMYSIGN}" -test_signed_pull "dummy" +test_signed_pull "dummy" "" +if ! has_libsodium; then + echo "ok ed25519-key pull signed commit # SKIP due libsodium unavailability" + echo "ok ed25519-key re-pull signature for stored commit # SKIP due libsodium unavailability" + echo "ok ed25519-file pull signed commit # SKIP due libsodium unavailability" + echo "ok ed25519-file re-pull signature for stored commit # SKIP due libsodium unavailability" + exit 0 +fi # Test ostree sign with 'ed25519' module gen_ed25519_keys @@ -80,7 +88,7 @@ COMMIT_ARGS="--sign=${SECRET} --sign-type=ed25519" repo_init --set=sign-verify=true ${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-key "${PUBLIC}" -test_signed_pull "ed25519" +test_signed_pull "ed25519" "key" # Prepare files with public ed25519 signatures PUBKEYS="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.ed25519)" @@ -95,6 +103,5 @@ echo ${PUBLIC} >> ${PUBKEYS} repo_init --set=sign-verify=true ${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-file "${PUBKEYS}" -test_signed_pull "ed25519" +test_signed_pull "ed25519" "file" -echo "ok verify ed25519 keys file" From 0bdcf14d567d6cd0abaa8e18d60a165c9f011aec Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 26 Nov 2019 12:44:44 +0300 Subject: [PATCH 49/75] lib/sign: new function for summary file signing Add function `ostree_sign_summary()` allowing to sign the summary file. Signed-off-by: Denis Pynkin --- apidoc/ostree-sections.txt | 1 + src/libostree/libostree-devel.sym | 1 + src/libostree/ostree-sign.c | 96 +++++++++++++++++++++++++++++++ src/libostree/ostree-sign.h | 6 ++ 4 files changed, 104 insertions(+) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 9b71d610..a5a632a5 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -723,6 +723,7 @@ ostree_sign_clear_keys ostree_sign_load_pk ostree_sign_set_pk ostree_sign_set_sk +ostree_sign_summary ostree_sign_get_type ostree_sign_dummy_get_type diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index a10ec266..29221f5d 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -35,6 +35,7 @@ global: ostree_sign_set_pk; ostree_sign_add_pk; ostree_sign_set_sk; + ostree_sign_summary; ostree_sign_dummy_get_type; ostree_sign_ed25519_get_type; } LIBOSTREE_2020.1; diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index 75db0b26..e7962425 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -48,6 +48,7 @@ #endif #include "ostree-autocleanups.h" +#include "ostree-repo-private.h" #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "OSTreeSign" @@ -594,3 +595,98 @@ ostree_sign_get_by_name (const gchar *name, GError **error) return sign; } + +/** + * ostree_sign_summary: + * @self: Self + * @repo: ostree repository + * @keys: keys -- GVariant containing keys as GVarints specific to signature type. + * @cancellable: A #GCancellable + * @error: a #GError + * + * Add a signature to a summary file. + * Based on ostree_repo_add_gpg_signature_summary implementation. + * + * Returns: @TRUE if summary file has been signed with all provided keys + */ +gboolean +ostree_sign_summary (OstreeSign *self, + OstreeRepo *repo, + GVariant *keys, + GCancellable *cancellable, + GError **error) +{ + g_debug ("%s enter", __FUNCTION__); + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); + + gboolean ret = FALSE; + + g_autoptr(GVariant) normalized = NULL; + g_autoptr(GBytes) summary_data = NULL; + g_autoptr(GVariant) metadata = NULL; + + glnx_autofd int fd = -1; + if (!glnx_openat_rdonly (repo->repo_dir_fd, "summary", TRUE, &fd, error)) + goto out; + summary_data = ot_fd_readall_or_mmap (fd, 0, error); + if (!summary_data) + goto out; + + /* Note that fd is reused below */ + glnx_close_fd (&fd); + + if (!ot_openat_ignore_enoent (repo->repo_dir_fd, "summary.sig", &fd, error)) + goto out; + if (fd >= 0) + { + if (!ot_variant_read_fd (fd, 0, OSTREE_SUMMARY_SIG_GVARIANT_FORMAT, + FALSE, &metadata, error)) + goto out; + } + + if (g_variant_n_children(keys) == 0) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No keys passed for signing summary"); + goto out; + } + + GVariantIter *iter; + GVariant *key; + + g_variant_get (keys, "av", &iter); + while (g_variant_iter_loop (iter, "v", &key)) + { + g_autoptr (GBytes) signature = NULL; + + if (!ostree_sign_set_sk (self, key, error)) + goto out; + + if (!ostree_sign_data (self, + summary_data, + &signature, + cancellable, + error)) + goto out; + + g_autoptr(GVariant) old_metadata = g_steal_pointer (&metadata); + metadata = + _sign_detached_metadata_append (self, old_metadata, signature); + } + g_variant_iter_free (iter); + + normalized = g_variant_get_normal_form (metadata); + if (!_ostree_repo_file_replace_contents (repo, + repo->repo_dir_fd, + "summary.sig", + g_variant_get_data (normalized), + g_variant_get_size (normalized), + cancellable, error)) + goto out; + + ret = TRUE; + +out: + return ret; +} diff --git a/src/libostree/ostree-sign.h b/src/libostree/ostree-sign.h index 1415becb..678f182d 100644 --- a/src/libostree/ostree-sign.h +++ b/src/libostree/ostree-sign.h @@ -152,5 +152,11 @@ GStrv ostree_sign_list_names(void); _OSTREE_PUBLIC OstreeSign * ostree_sign_get_by_name (const gchar *name, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sign_summary (OstreeSign *self, + OstreeRepo *repo, + GVariant *keys, + GCancellable *cancellable, + GError **error); G_END_DECLS From 137306f6f347788205d0b4756ac09709139a7b0c Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 26 Nov 2019 12:48:35 +0300 Subject: [PATCH 50/75] bin/summary: add signing with alternative mechanism Allow to sign the summary file with alternative signing mechanism. Added new options: - --sign-type -- select the engine (defaults to ed25519) - --sign -- secret key to use for signing Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-summary.c | 80 ++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/src/ostree/ot-builtin-summary.c b/src/ostree/ot-builtin-summary.c index 0f70f071..de6df835 100644 --- a/src/ostree/ot-builtin-summary.c +++ b/src/ostree/ot-builtin-summary.c @@ -27,10 +27,13 @@ #include "ot-builtins.h" #include "ostree.h" #include "otutil.h" +#include "ostree-sign.h" static gboolean opt_update, opt_view, opt_raw; -static char **opt_key_ids; +static char **opt_gpg_key_ids; static char *opt_gpg_homedir; +static char **opt_key_ids; +static char *opt_sign_name; static char **opt_metadata; /* ATTENTION: @@ -42,8 +45,10 @@ static GOptionEntry options[] = { { "update", 'u', 0, G_OPTION_ARG_NONE, &opt_update, "Update the summary", NULL }, { "view", 'v', 0, G_OPTION_ARG_NONE, &opt_view, "View the local summary file", NULL }, { "raw", 0, 0, G_OPTION_ARG_NONE, &opt_raw, "View the raw bytes of the summary file", NULL }, - { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the summary with", "KEY-ID"}, + { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_gpg_key_ids, "GPG Key ID to sign the summary with", "KEY-ID"}, { "gpg-homedir", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"}, + { "sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "Key ID to sign the summary with", "KEY-ID"}, + { "sign-type", 0, 0, G_OPTION_ARG_STRING, &opt_sign_name, "Signature type to use (defaults to 'ed25519')", "NAME"}, { "add-metadata", 'm', 0, G_OPTION_ARG_STRING_ARRAY, &opt_metadata, "Additional metadata field to add to the summary", "KEY=VALUE" }, { NULL } }; @@ -87,6 +92,7 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; + g_autoptr (OstreeSign) sign = NULL; OstreeDumpFlags flags = OSTREE_DUMP_NONE; context = g_option_context_new (""); @@ -94,6 +100,17 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) return FALSE; + /* Initialize crypto system */ + if (opt_key_ids) + { + if (!opt_sign_name) + opt_sign_name = "ed25519"; + + sign = ostree_sign_get_by_name (opt_sign_name, error); + if (sign == NULL) + return FALSE; + } + if (opt_update) { g_autoptr(GVariant) additional_metadata = NULL; @@ -164,10 +181,9 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati new_summary_commit, repo_file, &new_ostree_metadata_checksum, NULL, error)) return FALSE; - - if (opt_key_ids != NULL) + if (opt_gpg_key_ids != NULL) { - for (const char * const *iter = (const char * const *) opt_key_ids; + for (const char * const *iter = (const char * const *) opt_gpg_key_ids; iter != NULL && *iter != NULL; iter++) { const char *key_id = *iter; @@ -182,6 +198,27 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati } } + if (opt_key_ids) + { + char **iter; + for (iter = opt_key_ids; iter && *iter; iter++) + { + const char *keyid = *iter; + g_autoptr (GVariant) secret_key = NULL; + + secret_key = g_variant_new_string (keyid); + if (!ostree_sign_set_sk (sign, secret_key, error)) + return FALSE; + + if (!ostree_sign_commit (sign, + repo, + new_ostree_metadata_checksum, + cancellable, + error)) + return FALSE; + } + } + ostree_repo_transaction_set_collection_ref (repo, &collection_ref, new_ostree_metadata_checksum); @@ -194,16 +231,45 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati return FALSE; #ifndef OSTREE_DISABLE_GPGME - if (opt_key_ids) + if (opt_gpg_key_ids) { if (!ostree_repo_add_gpg_signature_summary (repo, - (const gchar **) opt_key_ids, + (const gchar **) opt_gpg_key_ids, opt_gpg_homedir, cancellable, error)) return FALSE; } #endif + if (opt_key_ids) + { + g_autoptr (GVariant) secret_keys = NULL; + g_autoptr (GVariantBuilder) sk_builder = NULL; + + sk_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + + char **iter; + for (iter = opt_key_ids; iter && *iter; iter++) + { + const char *keyid = *iter; + GVariant *secret_key = NULL; + + /* Currently only strings are used as keys + * for supported signature types */ + secret_key = g_variant_new_string (keyid); + + g_variant_builder_add (sk_builder, "v", secret_key); + } + + secret_keys = g_variant_builder_end (sk_builder); + + if (! ostree_sign_summary (sign, + repo, + secret_keys, + cancellable, + error)) + return FALSE; + } } else if (opt_view || opt_raw) { From 40b80344f87b1ddcbf0d8190fbaf80df956172c0 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 26 Nov 2019 09:57:35 +0000 Subject: [PATCH 51/75] lib/repo-pull: verify signature on summary pull Add signature verification on summary file pulling. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 180 ++++++++++++++++++++++--------- 1 file changed, 128 insertions(+), 52 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 78bb32f3..c6a94d72 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -108,6 +108,7 @@ typedef struct { gboolean gpg_verify; gboolean gpg_verify_summary; gboolean sign_verify; + gboolean sign_verify_summary; gboolean require_static_deltas; gboolean disable_static_deltas; gboolean has_tombstone_commits; @@ -1563,6 +1564,51 @@ _load_public_keys (OtPullData *pull_data, return (loaded_from_file || loaded_inlined); } +static gboolean +_ostree_repo_sign_verify (OtPullData *pull_data, + GBytes *signed_data, + GVariant *metadata) +{ + /* list all signature types in detached metadata and check if signed by any? */ + g_auto (GStrv) names = ostree_sign_list_names(); + for (char **iter=names; iter && *iter; iter++) + { + g_autoptr (OstreeSign) sign = NULL; + g_autoptr (GVariant) signatures = NULL; + const gchar *signature_key = NULL; + GVariantType *signature_format = NULL; + g_autoptr (GError) local_error = NULL; + + if ((sign = ostree_sign_get_by_name (*iter, &local_error)) == NULL) + continue; + + signature_key = ostree_sign_metadata_key (sign); + signature_format = (GVariantType *) ostree_sign_metadata_format (sign); + + signatures = g_variant_lookup_value (metadata, + signature_key, + signature_format); + + /* If not found signatures for requested signature subsystem */ + if (!signatures) + continue; + + /* Try to load public key(s) according remote's configuration */ + if (!_load_public_keys (pull_data, sign)) + continue; + + /* Return true if any signature fit to pre-loaded public keys. + * If no keys configured -- then system configuration will be used */ + if (ostree_sign_data_verify (sign, + signed_data, + signatures, + &local_error)) + return TRUE; + } + + return FALSE; +} + static gboolean ostree_verify_unwritten_commit (OtPullData *pull_data, const char *checksum, @@ -1572,21 +1618,24 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, GCancellable *cancellable, GError **error) { + + if (pull_data->gpg_verify || pull_data->sign_verify) + /* Shouldn't happen, but see comment in process_verify_result() */ + if (g_hash_table_contains (pull_data->verified_commits, checksum)) + return TRUE; + + g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); + #ifndef OSTREE_DISABLE_GPGME if (pull_data->gpg_verify) { const char *keyring_remote = NULL; - /* Shouldn't happen, but see comment in process_verify_result() */ - if (g_hash_table_contains (pull_data->verified_commits, checksum)) - return TRUE; - if (ref != NULL) keyring_remote = g_hash_table_lookup (pull_data->ref_keyring_map, ref); if (keyring_remote == NULL) keyring_remote = pull_data->remote_name; - g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); g_autoptr(OstreeGpgVerifyResult) result = _ostree_repo_gpg_verify_with_metadata (pull_data->repo, signed_data, detached_metadata, @@ -1606,59 +1655,17 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, "Can't verify commit without detached metadata"); return FALSE; } - /* Shouldn't happen, but see comment in process_verify_result() */ - if (g_hash_table_contains (pull_data->verified_commits, checksum)) - return TRUE; - gboolean ret = FALSE; - g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit); - - /* list all signature types in detached metadata and check if signed by any? */ - g_auto (GStrv) names = ostree_sign_list_names(); - for (guint i=0; i < g_strv_length (names); i++) + if (!_ostree_repo_sign_verify (pull_data, signed_data, detached_metadata)) { - g_autoptr (OstreeSign) sign = NULL; - g_autoptr (GVariant) signatures = NULL; - const gchar *signature_key = NULL; - GVariantType *signature_format = NULL; - g_autoptr (GError) local_error = NULL; - - if ((sign = ostree_sign_get_by_name (names[i], &local_error)) == NULL) - continue; - - signature_key = ostree_sign_metadata_key (sign); - signature_format = (GVariantType *) ostree_sign_metadata_format (sign); - - signatures = g_variant_lookup_value (detached_metadata, - signature_key, - signature_format); - - /* If not found signatures for requested signature subsystem */ - if (!signatures) - continue; - - /* Try to load public key(s) according remote's configuration */ - if (!_load_public_keys (pull_data, sign)) - continue; - - /* Return true if any signature fit to pre-loaded public keys. - * If no keys configured -- then system configuration will be used */ - if (ostree_sign_data_verify (sign, - signed_data, - signatures, - &local_error)) - ret = TRUE; + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Can't verify commit"); + return FALSE; } /* Mark the commit as verified to avoid double verification * see process_verify_result () for rationale */ - if (ret) - g_hash_table_add (pull_data->verified_commits, g_strdup (checksum)); - else - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't verify commit"); - - return ret; + g_hash_table_add (pull_data->verified_commits, g_strdup (checksum)); } return TRUE; @@ -3773,6 +3780,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, gboolean opt_gpg_verify_set = FALSE; gboolean opt_gpg_verify_summary_set = FALSE; gboolean opt_sign_verify_set = FALSE; + gboolean opt_sign_verify_summary_set = FALSE; gboolean opt_collection_refs_set = FALSE; gboolean opt_n_network_retries_set = FALSE; gboolean opt_ref_keyring_map_set = FALSE; @@ -3809,6 +3817,8 @@ ostree_repo_pull_with_options (OstreeRepo *self, g_variant_lookup (options, "gpg-verify-summary", "b", &pull_data->gpg_verify_summary); opt_sign_verify_set = g_variant_lookup (options, "sign-verify", "b", &pull_data->sign_verify); + opt_sign_verify_summary_set = + g_variant_lookup (options, "sign-verify-summary", "b", &pull_data->sign_verify_summary); (void) g_variant_lookup (options, "depth", "i", &pull_data->maxdepth); (void) g_variant_lookup (options, "disable-static-deltas", "b", &pull_data->disable_static_deltas); (void) g_variant_lookup (options, "require-static-deltas", "b", &pull_data->require_static_deltas); @@ -3996,6 +4006,11 @@ ostree_repo_pull_with_options (OstreeRepo *self, "sign-verify", FALSE, &pull_data->sign_verify, error)) goto out; + if (!opt_sign_verify_summary_set) + if (!ostree_repo_get_remote_boolean_option (self, pull_data->remote_name, + "sign-verify-summary", FALSE, + &pull_data->sign_verify_summary, error)) + goto out; /* NOTE: If changing this, see the matching implementation in * ostree-sysroot-upgrader.c @@ -4377,6 +4392,67 @@ ostree_repo_pull_with_options (OstreeRepo *self, } #endif /* OSTREE_DISABLE_GPGME */ + if (pull_data->sign_verify_summary) + { + if (!bytes_sig && pull_data->sign_verify_summary) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Signatures verification enabled, but no summary.sig found (use sign-verify-summary=false in remote config to disable)"); + goto out; + } + if (bytes_summary && bytes_sig) + { + g_autoptr(GVariant) signatures = NULL; + + signatures = g_variant_new_from_bytes (OSTREE_SUMMARY_SIG_GVARIANT_FORMAT, + bytes_sig, FALSE); + + + if (!_ostree_repo_sign_verify (pull_data, bytes_summary, signatures)) + { + gboolean ret = FALSE; + + if (summary_from_cache) + { + /* The cached summary doesn't match, fetch a new one and verify again */ + if ((self->test_error_flags & OSTREE_REPO_TEST_ERROR_INVALID_CACHE) > 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Remote %s cached summary invalid and " + "OSTREE_REPO_TEST_ERROR_INVALID_CACHE specified", + pull_data->remote_name); + goto out; + } + else + g_debug ("Remote %s cached summary invalid, pulling new version", + pull_data->remote_name); + + summary_from_cache = FALSE; + g_clear_pointer (&bytes_summary, (GDestroyNotify)g_bytes_unref); + if (!_ostree_fetcher_mirrored_request_to_membuf (pull_data->fetcher, + pull_data->meta_mirrorlist, + "summary", + OSTREE_FETCHER_REQUEST_OPTIONAL_CONTENT, + pull_data->n_network_retries, + &bytes_summary, + OSTREE_MAX_METADATA_SIZE, + cancellable, error)) + goto out; + + if (_ostree_repo_sign_verify (pull_data, bytes_summary, signatures)) + ret = TRUE; + } + + if (!ret) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Can't verify summary"); + goto out; + } + } + } + } + if (bytes_summary) { pull_data->summary_data = g_bytes_ref (bytes_summary); From 1de2efa2ed95cbcabdd58f15c974e3fd55c30a70 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 26 Nov 2019 13:01:36 +0300 Subject: [PATCH 52/75] tests/sign: new test for summary file verification Add test for signature verification of summary file during the pull. Adopted version of GPG tests from `test-pull-summary-sigs.sh`. Signed-off-by: Denis Pynkin --- Makefile-tests.am | 6 +- tests/test-signed-pull-summary.sh | 287 ++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 5 deletions(-) create mode 100755 tests/test-signed-pull-summary.sh diff --git a/Makefile-tests.am b/Makefile-tests.am index 505245cd..3270bd9c 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -138,13 +138,9 @@ _installed_or_uninstalled_test_scripts = \ tests/test-pull-collections.sh \ tests/test-config.sh \ tests/test-signed-commit.sh \ - $(NULL) - -if USE_LIBSODIUM -_installed_or_uninstalled_test_scripts += \ tests/test-signed-pull.sh \ + tests/test-signed-pull-summary.sh \ $(NULL) -endif if USE_GPGME _installed_or_uninstalled_test_scripts += \ diff --git a/tests/test-signed-pull-summary.sh b/tests/test-signed-pull-summary.sh new file mode 100755 index 00000000..7b18cf65 --- /dev/null +++ b/tests/test-signed-pull-summary.sh @@ -0,0 +1,287 @@ +#!/bin/bash +# +# Copyright (C) 2019 Collabora Ltd. +# +# SPDX-License-Identifier: LGPL-2.0+ +# +# This library 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 of the License, or (at your option) any later version. +# +# This library 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 a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# Based on test-pull-summary-sigs.sh test. + +set -euo pipefail + +. $(dirname $0)/libtest.sh + +echo "1..14" + +repo_reinit () { + ARGS="$*" + cd ${test_tmpdir} + rm -rf repo + mkdir repo + ostree_repo_init repo --mode=archive + ${OSTREE} --repo=repo remote add \ + --set=gpg-verify=false --set=gpg-verify-summary=false \ + --set=sign-verify=false --set=sign-verify-summary=true \ + ${ARGS} origin $(cat httpd-address)/ostree/gnomerepo +} + +for engine in dummy ed25519 +do + case "${engine}" in + dummy) + # Tests with dummy engine + SIGN_KEY="dummysign" + PUBLIC_KEY="dummysign" + ;; + ed25519) + if ! has_libsodium; then + echo "ok ${engine} pull mirror summary # SKIP due libsodium unavailability" + echo "ok ${engine} pull with signed summary # SKIP due libsodium unavailability" + echo "ok ${engine} prune summary cache # SKIP due libsodium unavailability" + echo "ok ${engine} pull with signed summary and cachedir # SKIP due libsodium unavailability" + echo "ok ${engine} pull with invalid ${engine} summary signature fails # SKIP due libsodium unavailability" + echo "ok ${engine} pull delta with signed summary # SKIP due libsodium unavailability" + + continue + fi + gen_ed25519_keys + SIGN_KEY="${ED25519SECRET}" + PUBLIC_KEY="${ED25519PUBLIC}" + ;; + *) + fatal "Unsupported engine ${engine}" + ;; + esac + + COMMIT_SIGN="--sign-type=${engine} --sign=${SIGN_KEY}" + + # clenup the testdir prior the next engine + rm -rf ${test_tmpdir}/ostree-srv ${test_tmpdir}/gnomerepo ${test_tmpdir}/httpd ${test_tmpdir}/repo ${test_tmpdir}/cachedir\ + ${test_tmpdir}/main-copy ${test_tmpdir}/other-copy ${test_tmpdir}/yet-another-copy + + setup_fake_remote_repo1 "archive" "${COMMIT_SIGN}" + + # Now, setup multiple branches + mkdir ${test_tmpdir}/ostree-srv/other-files + cd ${test_tmpdir}/ostree-srv/other-files + echo 'hello world another object' > hello-world + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo commit ${COMMIT_SIGN} -b other -s "A commit" -m "Another Commit body" + + mkdir ${test_tmpdir}/ostree-srv/yet-other-files + cd ${test_tmpdir}/ostree-srv/yet-other-files + echo 'hello world yet another object' > yet-another-hello-world + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo commit ${COMMIT_SIGN} -b yet-another -s "A commit" -m "Another Commit body" + + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u + + prev_dir=`pwd` + cd ${test_tmpdir} + ostree_repo_init repo --mode=archive + ${CMD_PREFIX} ostree --repo=repo remote add \ + --set=gpg-verify=false --set=gpg-verify-summary=false \ + --set=sign-verify=false --set=sign-verify-summary=false \ + origin $(cat httpd-address)/ostree/gnomerepo + ${CMD_PREFIX} ostree --repo=repo pull --mirror origin + assert_has_file repo/summary + ${CMD_PREFIX} ostree --repo=repo checkout -U main main-copy + assert_file_has_content main-copy/baz/cow "moo" + ${CMD_PREFIX} ostree --repo=repo checkout -U other other-copy + assert_file_has_content other-copy/hello-world "hello world another object" + ${CMD_PREFIX} ostree --repo=repo checkout -U yet-another yet-another-copy + assert_file_has_content yet-another-copy/yet-another-hello-world "hello world yet another object" + ${CMD_PREFIX} ostree --repo=repo fsck + echo "ok ${engine} pull mirror summary" + + + cd $prev_dir + + ${OSTREE} --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u ${COMMIT_SIGN} + + cd ${test_tmpdir} + repo_reinit --set=verification-key=${PUBLIC_KEY} + ${OSTREE} --repo=repo pull origin main + assert_has_file repo/tmp/cache/summaries/origin + assert_has_file repo/tmp/cache/summaries/origin.sig + + rm repo/tmp/cache/summaries/origin + ${OSTREE} --repo=repo pull origin main + assert_has_file repo/tmp/cache/summaries/origin + + echo "ok ${engine} pull with signed summary" + + touch repo/tmp/cache/summaries/foo + touch repo/tmp/cache/summaries/foo.sig + ${OSTREE} --repo=repo prune + assert_not_has_file repo/tmp/cache/summaries/foo + assert_not_has_file repo/tmp/cache/summaries/foo.sig + assert_has_file repo/tmp/cache/summaries/origin + assert_has_file repo/tmp/cache/summaries/origin.sig + echo "ok ${engine} prune summary cache" + + cd ${test_tmpdir} + repo_reinit --set=verification-key=${PUBLIC_KEY} + mkdir cachedir + ${OSTREE} --repo=repo pull --cache-dir=cachedir origin main + assert_not_has_file repo/tmp/cache/summaries/origin + assert_not_has_file repo/tmp/cache/summaries/origin.sig + assert_has_file cachedir/summaries/origin + assert_has_file cachedir/summaries/origin.sig + + rm cachedir/summaries/origin + ${OSTREE} --repo=repo pull --cache-dir=cachedir origin main + assert_not_has_file repo/tmp/cache/summaries/origin + assert_has_file cachedir/summaries/origin + + echo "ok ${engine} pull with signed summary and cachedir" + + cd ${test_tmpdir} + repo_reinit --set=verification-key=${PUBLIC_KEY} + mv ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{,.good} + echo invalid > ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig + if ${OSTREE} --repo=repo pull origin main 2>err.txt; then + assert_not_reached "Successful pull with invalid ${engine} signature" + fi + assert_file_has_content err.txt "Can't verify summary" + mv ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.good,} + echo "ok ${engine} pull with invalid ${engine} summary signature fails" + + # Generate a delta + ${OSTREE} --repo=${test_tmpdir}/ostree-srv/gnomerepo static-delta generate --empty main + ${OSTREE} --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u ${COMMIT_SIGN} + + cd ${test_tmpdir} + repo_reinit --set=verification-key=${PUBLIC_KEY} + ${OSTREE} --repo=repo pull origin main + echo "ok ${engine} pull delta with signed summary" + +done + +if ! has_libsodium; then + echo "ok ${engine} pull with signed summary remote old summary # SKIP due libsodium unavailability" + echo "ok ${engine} pull with signed summary broken cache # SKIP due libsodium unavailability" + exit 0 +fi + +gen_ed25519_keys +SIGN_KEY="${ED25519SECRET}" +PUBLIC_KEY="${ED25519PUBLIC}" +COMMIT_SIGN="--sign-type=${engine} --sign=${SIGN_KEY}" + + +# Verify 'ostree remote summary' output. +${OSTREE} --repo=repo remote summary origin > summary.txt +assert_file_has_content summary.txt "* main" +assert_file_has_content summary.txt "* other" +assert_file_has_content summary.txt "* yet-another" +grep static-deltas summary.txt > static-deltas.txt +assert_file_has_content static-deltas.txt \ + $(${OSTREE} --repo=repo rev-parse origin:main) + +## Tests for handling of cached summaries while racing with remote summary updates + +# Make 2 different but valid summary/signature pairs to test races with +${OSTREE} --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u ${COMMIT_SIGN} +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary{,.1} +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{,.1} +mkdir ${test_tmpdir}/ostree-srv/even-another-files +cd ${test_tmpdir}/ostree-srv/even-another-files +echo 'hello world even another object' > even-another-hello-world +${OSTREE} --repo=${test_tmpdir}/ostree-srv/gnomerepo commit ${COMMIT_SIGN} -b even-another -s "A commit" -m "Another Commit body" +${OSTREE} --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u ${COMMIT_SIGN} +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary{,.2} +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{,.2} +cd ${test_tmpdir} + +# Reset to the old valid summary and pull to cache it +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary{.1,} +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.1,} +repo_reinit --set=verification-key=${PUBLIC_KEY} +${OSTREE} --repo=repo pull origin main +assert_has_file repo/tmp/cache/summaries/origin +assert_has_file repo/tmp/cache/summaries/origin.sig +cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.1 >&2 +cmp repo/tmp/cache/summaries/origin.sig ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig.1 >&2 + +# Simulate a pull race where the client gets the old summary and the new +# summary signature since it was generated on the server between the +# requests +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.2,} +if ${OSTREE} --repo=repo pull origin main 2>err.txt; then + assert_not_reached "Successful pull with old summary" +fi +assert_file_has_content err.txt "Can't verify summary" +assert_has_file repo/tmp/cache/summaries/origin +assert_has_file repo/tmp/cache/summaries/origin.sig +cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.1 >&2 +cmp repo/tmp/cache/summaries/origin.sig ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig.1 >&2 + +# Publish correct summary and check that subsequent pull succeeds +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary{.2,} +${OSTREE} --repo=repo pull origin main +assert_has_file repo/tmp/cache/summaries/origin +assert_has_file repo/tmp/cache/summaries/origin.sig +cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.2 >&2 +cmp repo/tmp/cache/summaries/origin.sig ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig.2 >&2 + +echo "ok ${engine} pull with signed summary remote old summary" + + +# Reset to the old valid summary and pull to cache it +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary{.1,} +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.1,} +repo_reinit --set=verification-key=${PUBLIC_KEY} +${OSTREE} --repo=repo pull origin main +assert_has_file repo/tmp/cache/summaries/origin +assert_has_file repo/tmp/cache/summaries/origin.sig +cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.1 >&2 +cmp repo/tmp/cache/summaries/origin.sig ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig.1 >&2 + +# Simulate a broken summary cache to see if it can be recovered from. +# Prior to commit c4c2b5eb the client would save the summary to the +# cache before validating the signature. That would mean the cache would +# have mismatched summary and signature and ostree would remain +# deadlocked there until the remote published a new signature. +# +# First pull with OSTREE_REPO_TEST_ERROR=invalid-cache to see the +# invalid cache is detected. Then pull again to check if it can be +# recovered from. +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.2 repo/tmp/cache/summaries/origin +if OSTREE_REPO_TEST_ERROR=invalid-cache ${OSTREE} --repo=repo pull origin main 2>err.txt; then + assert_not_reached "Should have hit OSTREE_REPO_TEST_ERROR_INVALID_CACHE" +fi +assert_file_has_content err.txt "OSTREE_REPO_TEST_ERROR_INVALID_CACHE" +assert_has_file repo/tmp/cache/summaries/origin +assert_has_file repo/tmp/cache/summaries/origin.sig +cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.2 >&2 +cmp repo/tmp/cache/summaries/origin.sig ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig.1 >&2 +${OSTREE} --repo=repo pull origin main +assert_has_file repo/tmp/cache/summaries/origin +assert_has_file repo/tmp/cache/summaries/origin.sig +cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.1 >&2 +cmp repo/tmp/cache/summaries/origin.sig ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig.1 >&2 + +# Publish new signature and check that subsequent pull succeeds +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary{.2,} +cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.2,} +${OSTREE} --repo=repo pull origin main +assert_has_file repo/tmp/cache/summaries/origin +assert_has_file repo/tmp/cache/summaries/origin.sig +cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.2 >&2 +cmp repo/tmp/cache/summaries/origin.sig ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig.2 >&2 + +echo "ok ${engine} pull with signed summary broken cache" + From 65c16a8318b992b436fb666d675eb195fe3cfc71 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 26 Nov 2019 13:35:30 +0300 Subject: [PATCH 53/75] man: add signature options for ostree summary Add a description of new options `--sign-type` and `--sign` for `ostree summary` command. Signed-off-by: Denis Pynkin --- man/ostree-summary.xml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/man/ostree-summary.xml b/man/ostree-summary.xml index 387dacd7..8305b4e1 100644 --- a/man/ostree-summary.xml +++ b/man/ostree-summary.xml @@ -51,7 +51,7 @@ Boston, MA 02111-1307, USA. - ostree summary --gpg-sign=KEYID --gpg-homedir=HOMEDIR --update --add-metadata=KEY=VALUE + ostree summary --gpg-sign=KEYID --gpg-homedir=HOMEDIR --sign=KEYID --sign-type=ENGINE --update --add-metadata=KEY=VALUE @@ -139,6 +139,39 @@ Boston, MA 02111-1307, USA. + + =ENGINE + + Use particular signature engine. Currently + available ed25519 and dummy + signature types. + + The default is ed25519. + + + + + ="KEY-ID" + + There KEY-ID is: + + + + + base64-encoded secret key for commit signing. + + + + + + + ASCII-string used as secret key. + + + + + + From 809176b1ffa4d8b167f6fca26b7f697795eedcff Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 27 Nov 2019 12:21:39 +0300 Subject: [PATCH 54/75] gpg: do not fail GPG-related configuration get for remote We don't need anymore stubs for verification options for remotes in case if ostree built without GPG support. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 9 --------- src/libostree/ostree-repo.c | 18 ------------------ 2 files changed, 27 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index c6a94d72..203b34cf 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -3986,7 +3986,6 @@ ostree_repo_pull_with_options (OstreeRepo *self, g_free (pull_data->remote_name); pull_data->remote_name = g_strdup (remote_name_or_baseurl); -#ifndef OSTREE_DISABLE_GPGME /* Fetch GPG verification settings from remote if it wasn't already * explicitly set in the options. */ if (!opt_gpg_verify_set) @@ -3998,7 +3997,6 @@ ostree_repo_pull_with_options (OstreeRepo *self, if (!ostree_repo_remote_get_gpg_verify_summary (self, pull_data->remote_name, &pull_data->gpg_verify_summary, error)) goto out; -#endif /* OSTREE_DISABLE_GPGME */ /* Fetch verification settings from remote if it wasn't already * explicitly set in the options. */ if (!opt_sign_verify_set) @@ -6460,9 +6458,7 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, g_autofree char *metalink_url_string = NULL; g_autoptr(GBytes) summary = NULL; g_autoptr(GBytes) signatures = NULL; -#ifndef OSTREE_DISABLE_GPGME gboolean gpg_verify_summary; -#endif gboolean ret = FALSE; gboolean summary_is_from_cache; @@ -6484,7 +6480,6 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, error)) goto out; -#ifndef OSTREE_DISABLE_GPGME if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error)) goto out; @@ -6538,10 +6533,6 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, } } -#else - g_message ("%s: GPG feature is disabled in a build time", __FUNCTION__); -#endif /* OSTREE_DISABLE_GPGME */ - if (out_summary != NULL) *out_summary = g_steal_pointer (&summary); diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 3aeecc5c..36fde3dc 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -2028,17 +2028,8 @@ ostree_repo_remote_get_gpg_verify (OstreeRepo *self, return TRUE; } -#ifndef OSTREE_DISABLE_GPGME return ostree_repo_get_remote_boolean_option (self, name, "gpg-verify", TRUE, out_gpg_verify, error); -#else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); - if (out_gpg_verify != NULL) - *out_gpg_verify = FALSE; - return FALSE; -#endif /* OSTREE_DISABLE_GPGME */ } /** @@ -2060,17 +2051,8 @@ ostree_repo_remote_get_gpg_verify_summary (OstreeRepo *self, gboolean *out_gpg_verify_summary, GError **error) { -#ifndef OSTREE_DISABLE_GPGME return ostree_repo_get_remote_boolean_option (self, name, "gpg-verify-summary", FALSE, out_gpg_verify_summary, error); -#else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); - if (out_gpg_verify_summary != NULL) - *out_gpg_verify_summary = FALSE; - return FALSE; -#endif /* OSTREE_DISABLE_GPGME */ } /** From df36984684e84551f1db944a48af0978aee3469d Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 27 Nov 2019 13:15:26 +0000 Subject: [PATCH 55/75] lib/repo-pull: change sign supporting functions Change the API of supporting functions `_load_public_keys()` and `_ostree_repo_sign_verify()` -- pass repo object and remote name instead of OtPullData object. This allows to use these functions not only in pull-related places. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 203b34cf..311e37d8 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1483,8 +1483,9 @@ process_verify_result (OtPullData *pull_data, * Returns: %TRUE if no configuration or any key loaded. * */ static gboolean -_load_public_keys (OtPullData *pull_data, - OstreeSign *sign) +_load_public_keys (OstreeSign *sign, + OstreeRepo *repo, + const gchar *remote_name) { g_autofree gchar *pk_ascii = NULL; @@ -1493,13 +1494,13 @@ _load_public_keys (OtPullData *pull_data, gboolean loaded_inlined = TRUE; g_autoptr (GError) error = NULL; - ostree_repo_get_remote_option (pull_data->repo, - pull_data->remote_name, + ostree_repo_get_remote_option (repo, + remote_name, "verification-file", NULL, &pk_file, NULL); - ostree_repo_get_remote_option (pull_data->repo, - pull_data->remote_name, + ostree_repo_get_remote_option (repo, + remote_name, "verification-key", NULL, &pk_ascii, NULL); @@ -1565,7 +1566,8 @@ _load_public_keys (OtPullData *pull_data, } static gboolean -_ostree_repo_sign_verify (OtPullData *pull_data, +_ostree_repo_sign_verify (OstreeRepo *repo, + const gchar *remote_name, GBytes *signed_data, GVariant *metadata) { @@ -1594,7 +1596,7 @@ _ostree_repo_sign_verify (OtPullData *pull_data, continue; /* Try to load public key(s) according remote's configuration */ - if (!_load_public_keys (pull_data, sign)) + if (!_load_public_keys (sign, repo, remote_name)) continue; /* Return true if any signature fit to pre-loaded public keys. @@ -1656,7 +1658,7 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, return FALSE; } - if (!_ostree_repo_sign_verify (pull_data, signed_data, detached_metadata)) + if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, signed_data, detached_metadata)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't verify commit"); @@ -2012,7 +2014,7 @@ scan_commit_object (OtPullData *pull_data, continue; /* Try to load public key(s) according remote's configuration */ - if (!_load_public_keys (pull_data, sign)) + if (!_load_public_keys (sign, pull_data->repo, pull_data->remote_name)) continue; /* Set return to true if any sign fit */ @@ -4406,7 +4408,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, bytes_sig, FALSE); - if (!_ostree_repo_sign_verify (pull_data, bytes_summary, signatures)) + if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures)) { gboolean ret = FALSE; @@ -4437,7 +4439,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, cancellable, error)) goto out; - if (_ostree_repo_sign_verify (pull_data, bytes_summary, signatures)) + if (_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures)) ret = TRUE; } From c69dce3c314e534dbebfaff946f0ca170a3ad7a7 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 27 Nov 2019 13:24:01 +0000 Subject: [PATCH 56/75] lib/repo-pull: set default for sign-verify-summary Use FALSE as default for summary verification while pulling from remote. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 311e37d8..e666b0c8 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -6314,6 +6314,7 @@ ostree_repo_pull_from_remotes_async (OstreeRepo *self, #endif /* OSTREE_DISABLE_GPGME */ g_variant_dict_insert (&local_options_dict, "gpg-verify-summary", "b", FALSE); g_variant_dict_insert (&local_options_dict, "sign-verify", "b", FALSE); + g_variant_dict_insert (&local_options_dict, "sign-verify-summary", "b", FALSE); g_variant_dict_insert (&local_options_dict, "inherit-transaction", "b", TRUE); if (result->remote->refspec_name != NULL) g_variant_dict_insert (&local_options_dict, "override-remote-name", "s", result->remote->refspec_name); From b97ab81bab817c03cb047844b4b8518e45afaf65 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 27 Nov 2019 16:26:54 +0300 Subject: [PATCH 57/75] lib/repo-pull: add signature check while fetching summary Check the signature of downloaded summary file. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 81 ++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index e666b0c8..0c33b0f6 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -6462,6 +6462,7 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, g_autoptr(GBytes) summary = NULL; g_autoptr(GBytes) signatures = NULL; gboolean gpg_verify_summary; + gboolean sign_verify_summary; gboolean ret = FALSE; gboolean summary_is_from_cache; @@ -6486,33 +6487,73 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error)) goto out; - if (gpg_verify_summary && summary == NULL) + if (gpg_verify_summary) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "GPG verification enabled, but no summary found (check that the configured URL in remote config is correct)"); - goto out; + if (summary == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "GPG verification enabled, but no summary found (check that the configured URL in remote config is correct)"); + goto out; + } + + if (signatures == NULL) + { + g_set_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE, + "GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)"); + goto out; + } + + /* Verify any summary signatures. */ + if (summary != NULL && signatures != NULL) + { + g_autoptr(OstreeGpgVerifyResult) result = NULL; + + result = ostree_repo_verify_summary (self, + name, + summary, + signatures, + cancellable, + error); + if (!ostree_gpg_verify_result_require_valid_signature (result, error)) + goto out; + } } - if (gpg_verify_summary && signatures == NULL) - { - g_set_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE, - "GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)"); + if (!ostree_repo_get_remote_boolean_option (self, name, "sign-verify-summary", + FALSE, &sign_verify_summary, error)) goto out; - } - /* Verify any summary signatures. */ - if (gpg_verify_summary && summary != NULL && signatures != NULL) + if (sign_verify_summary) { - g_autoptr(OstreeGpgVerifyResult) result = NULL; + if (summary == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Signature verification enabled, but no summary found (check that the configured URL in remote config is correct)"); + goto out; + } - result = ostree_repo_verify_summary (self, - name, - summary, - signatures, - cancellable, - error); - if (!ostree_gpg_verify_result_require_valid_signature (result, error)) - goto out; + if (signatures == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Signature verification enabled, but no summary signatures found (use sign-verify-summary=false in remote config to disable)"); + goto out; + } + + /* Verify any summary signatures. */ + if (summary != NULL && signatures != NULL) + { + g_autoptr(GVariant) sig_variant = NULL; + + sig_variant = g_variant_new_from_bytes (OSTREE_SUMMARY_SIG_GVARIANT_FORMAT, + signatures, FALSE); + + if (!_ostree_repo_sign_verify (self, name, summary, sig_variant)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Signature verification enabled, but no valid signatures found"); + goto out; + } + } } if (!summary_is_from_cache && summary && signatures) From 8b3b35a04a72f50d09c4facf6fd5d51205e5408c Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 27 Nov 2019 16:46:24 +0300 Subject: [PATCH 58/75] bin/pull-local: add --sign-verify-summary Add option for enabling summary file verification while pulling from local. Signed-off-by: Denis Pynkin --- src/ostree/ot-builtin-pull-local.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ostree/ot-builtin-pull-local.c b/src/ostree/ot-builtin-pull-local.c index 4dbd3bfd..695b09e5 100644 --- a/src/ostree/ot-builtin-pull-local.c +++ b/src/ostree/ot-builtin-pull-local.c @@ -40,6 +40,7 @@ static gboolean opt_require_static_deltas; static gboolean opt_gpg_verify; static gboolean opt_gpg_verify_summary; static gboolean opt_sign_verify; +static gboolean opt_sign_verify_summary; static int opt_depth = 0; /* ATTENTION: @@ -57,6 +58,7 @@ static GOptionEntry options[] = { { "gpg-verify", 0, 0, G_OPTION_ARG_NONE, &opt_gpg_verify, "GPG verify commits (must specify --remote)", NULL }, { "gpg-verify-summary", 0, 0, G_OPTION_ARG_NONE, &opt_gpg_verify_summary, "GPG verify summary (must specify --remote)", NULL }, { "sign-verify", 0, 0, G_OPTION_ARG_NONE, &opt_sign_verify, "Verify commits signature (must specify --remote)", NULL }, + { "sign-verify-summary", 0, 0, G_OPTION_ARG_NONE, &opt_sign_verify, "Verify summary signature (must specify --remote)", NULL }, { "depth", 0, 0, G_OPTION_ARG_INT, &opt_depth, "Traverse DEPTH parents (-1=infinite) (default: 0)", "DEPTH" }, { NULL } }; @@ -187,6 +189,9 @@ ostree_builtin_pull_local (int argc, char **argv, OstreeCommandInvocation *invoc if (opt_sign_verify) g_variant_builder_add (&builder, "{s@v}", "sign-verify", g_variant_new_variant (g_variant_new_boolean (TRUE))); + if (opt_sign_verify_summary) + g_variant_builder_add (&builder, "{s@v}", "sign-verify-summary", + g_variant_new_variant (g_variant_new_boolean (TRUE))); if (console.is_tty) progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console); From 4d0e3a66c5ec6cf1063a057a8e0ccf3d8be3d615 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 4 Dec 2019 20:40:55 +0000 Subject: [PATCH 59/75] lib/sign: make dummy engine non-public Remove unneeded public declaration for dummy signing engine. Signed-off-by: Denis Pynkin --- apidoc/ostree-sections.txt | 2 -- src/libostree/libostree-devel.sym | 1 - src/libostree/ostree-sign-dummy.c | 14 +++++++------- src/libostree/ostree-sign-dummy.h | 9 ++++----- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index a5a632a5..e61b46c3 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -709,7 +709,6 @@ ostree_kernel_args_to_string
ostree-sign OstreeSign -OstreeSignDummy OstreeSignEd25519 ostree_sign_list_names ostree_sign_commit @@ -726,6 +725,5 @@ ostree_sign_set_sk ostree_sign_summary ostree_sign_get_type -ostree_sign_dummy_get_type ostree_sign_ed25519_get_type
diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 29221f5d..730eac3e 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -36,7 +36,6 @@ global: ostree_sign_add_pk; ostree_sign_set_sk; ostree_sign_summary; - ostree_sign_dummy_get_type; ostree_sign_ed25519_get_type; } LIBOSTREE_2020.1; diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index 034c7d6b..df5a7b82 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -50,7 +50,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSignDummy, g_object_unref) static void ostree_sign_dummy_iface_init (OstreeSignInterface *self); -G_DEFINE_TYPE_WITH_CODE (OstreeSignDummy, ostree_sign_dummy, G_TYPE_OBJECT, +G_DEFINE_TYPE_WITH_CODE (OstreeSignDummy, _ostree_sign_dummy, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (OSTREE_TYPE_SIGN, ostree_sign_dummy_iface_init)); static void @@ -70,13 +70,13 @@ ostree_sign_dummy_iface_init (OstreeSignInterface *self) } static void -ostree_sign_dummy_class_init (OstreeSignDummyClass *self) +_ostree_sign_dummy_class_init (OstreeSignDummyClass *self) { g_debug ("%s enter", __FUNCTION__); } static void -ostree_sign_dummy_init (OstreeSignDummy *self) +_ostree_sign_dummy_init (OstreeSignDummy *self) { g_debug ("%s enter", __FUNCTION__); @@ -88,7 +88,7 @@ gboolean ostree_sign_dummy_set_sk (OstreeSign *self, GVariant *key, GError **err { g_debug ("%s enter", __FUNCTION__); - OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); g_free(sign->sk_ascii); @@ -101,7 +101,7 @@ gboolean ostree_sign_dummy_set_pk (OstreeSign *self, GVariant *key, GError **err { g_debug ("%s enter", __FUNCTION__); - OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); g_free(sign->pk_ascii); @@ -120,7 +120,7 @@ gboolean ostree_sign_dummy_data (OstreeSign *self, g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); *signature = g_bytes_new (sign->sk_ascii, strlen(sign->sk_ascii)); @@ -158,7 +158,7 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (data != NULL, FALSE); - OstreeSignDummy *sign = ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); + OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); gboolean ret = FALSE; diff --git a/src/libostree/ostree-sign-dummy.h b/src/libostree/ostree-sign-dummy.h index 6fc3a363..c37bcdfa 100644 --- a/src/libostree/ostree-sign-dummy.h +++ b/src/libostree/ostree-sign-dummy.h @@ -30,17 +30,16 @@ G_BEGIN_DECLS -#define OSTREE_TYPE_SIGN_DUMMY (ostree_sign_dummy_get_type ()) +#define OSTREE_TYPE_SIGN_DUMMY (_ostree_sign_dummy_get_type ()) -_OSTREE_PUBLIC -GType ostree_sign_dummy_get_type (void); +GType _ostree_sign_dummy_get_type (void); G_GNUC_BEGIN_IGNORE_DEPRECATIONS typedef struct _OstreeSignDummy OstreeSignDummy; typedef struct { GObjectClass parent_class; } OstreeSignDummyClass; -static inline OstreeSignDummy *OSTREE_SIGN_DUMMY (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_sign_dummy_get_type (), OstreeSignDummy); } -static inline gboolean OSTREE_IS_SIGN_DUMMY (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_sign_dummy_get_type ()); } +static inline OstreeSignDummy *OSTREE_SIGN_DUMMY (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, _ostree_sign_dummy_get_type (), OstreeSignDummy); } +static inline gboolean OSTREE_IS_SIGN_DUMMY (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, _ostree_sign_dummy_get_type ()); } G_GNUC_END_IGNORE_DEPRECATIONS From b4050b4a34721f1302ade2f6a82a1148a243b46c Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Wed, 4 Dec 2019 20:42:52 +0000 Subject: [PATCH 60/75] lib/sign: make ed25519 engine non-public Remove unneeded public declaration for ed25519 signing engine. Signed-off-by: Denis Pynkin --- apidoc/ostree-sections.txt | 2 -- src/libostree/libostree-devel.sym | 1 - src/libostree/ostree-sign-ed25519.c | 18 +++++++++--------- src/libostree/ostree-sign-ed25519.h | 9 ++++----- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index e61b46c3..4dcdc482 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -709,7 +709,6 @@ ostree_kernel_args_to_string
ostree-sign OstreeSign -OstreeSignEd25519 ostree_sign_list_names ostree_sign_commit ostree_sign_commit_verify @@ -725,5 +724,4 @@ ostree_sign_set_sk ostree_sign_summary ostree_sign_get_type -ostree_sign_ed25519_get_type
diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 730eac3e..4348ab8d 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -36,7 +36,6 @@ global: ostree_sign_add_pk; ostree_sign_set_sk; ostree_sign_summary; - ostree_sign_ed25519_get_type; } LIBOSTREE_2020.1; /* Stub section for the stable release *after* this development one; don't diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 2d5bdb16..d28a5c32 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -55,7 +55,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSignEd25519, g_object_unref) static void ostree_sign_ed25519_iface_init (OstreeSignInterface *self); -G_DEFINE_TYPE_WITH_CODE (OstreeSignEd25519, ostree_sign_ed25519, G_TYPE_OBJECT, +G_DEFINE_TYPE_WITH_CODE (OstreeSignEd25519, _ostree_sign_ed25519, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (OSTREE_TYPE_SIGN, ostree_sign_ed25519_iface_init)); static void @@ -76,13 +76,13 @@ ostree_sign_ed25519_iface_init (OstreeSignInterface *self) } static void -ostree_sign_ed25519_class_init (OstreeSignEd25519Class *self) +_ostree_sign_ed25519_class_init (OstreeSignEd25519Class *self) { g_debug ("%s enter", __FUNCTION__); } static void -ostree_sign_ed25519_init (OstreeSignEd25519 *self) +_ostree_sign_ed25519_init (OstreeSignEd25519 *self) { g_debug ("%s enter", __FUNCTION__); @@ -111,7 +111,7 @@ gboolean ostree_sign_ed25519_data (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)); + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); #ifdef HAVE_LIBSODIUM guchar *sig = NULL; @@ -163,7 +163,7 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, g_return_val_if_fail (data != NULL, FALSE); gboolean ret = FALSE; - OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); if (signatures == NULL) { @@ -284,7 +284,7 @@ gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); #ifdef HAVE_LIBSODIUM - OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); /* Clear secret key */ if (sign->secret_key != NULL) @@ -326,7 +326,7 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); #ifdef HAVE_LIBSODIUM - OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); ostree_sign_ed25519_clear_keys (self, error); @@ -391,7 +391,7 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); #ifdef HAVE_LIBSODIUM - OstreeSignEd25519 *sign = ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); g_autofree char * hex = NULL; gpointer key = NULL; @@ -446,7 +446,7 @@ _ed25519_add_revoked (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)); + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); g_autofree char * hex = NULL; gpointer key = NULL; diff --git a/src/libostree/ostree-sign-ed25519.h b/src/libostree/ostree-sign-ed25519.h index 6e5dd665..76c7e14d 100644 --- a/src/libostree/ostree-sign-ed25519.h +++ b/src/libostree/ostree-sign-ed25519.h @@ -30,17 +30,16 @@ G_BEGIN_DECLS -#define OSTREE_TYPE_SIGN_ED25519 (ostree_sign_ed25519_get_type ()) +#define OSTREE_TYPE_SIGN_ED25519 (_ostree_sign_ed25519_get_type ()) -_OSTREE_PUBLIC -GType ostree_sign_ed25519_get_type (void); +GType _ostree_sign_ed25519_get_type (void); G_GNUC_BEGIN_IGNORE_DEPRECATIONS typedef struct _OstreeSignEd25519 OstreeSignEd25519; typedef struct { GObjectClass parent_class; } OstreeSignEd25519Class; -static inline OstreeSignEd25519 *OSTREE_SIGN_ED25519 (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_sign_ed25519_get_type (), OstreeSignEd25519); } -static inline gboolean OSTREE_IS_SIGN_ED25519 (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_sign_ed25519_get_type ()); } +static inline OstreeSignEd25519 *OSTREE_SIGN_ED25519 (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, _ostree_sign_ed25519_get_type (), OstreeSignEd25519); } +static inline gboolean OSTREE_IS_SIGN_ED25519 (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, _ostree_sign_ed25519_get_type ()); } G_GNUC_END_IGNORE_DEPRECATIONS From dd27461e221748c745ad3e63f787cf3f8b98cdde Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 6 Dec 2019 15:04:14 +0300 Subject: [PATCH 61/75] lib/sign: better error handling of ed25519 initialization Add more precise error handling for ed25519 initialization. Check the initialization status at the beginning of every public function provided by ed25519 engine. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-ed25519.c | 98 ++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index d28a5c32..6bf0b10b 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -39,10 +39,17 @@ #define OSTREE_SIGN_METADATA_ED25519_KEY "ostree.sign.ed25519" #define OSTREE_SIGN_METADATA_ED25519_TYPE "aay" +typedef enum +{ + ED25519_OK, + ED25519_NOT_SUPPORTED, + ED25519_FAILED_INITIALIZATION +} ed25519_state; + struct _OstreeSignEd25519 { GObject parent; - gboolean initialized; + ed25519_state state; guchar *secret_key; GList *public_keys; GList *revoked_keys; @@ -86,22 +93,39 @@ _ostree_sign_ed25519_init (OstreeSignEd25519 *self) { g_debug ("%s enter", __FUNCTION__); - self->initialized = TRUE; + self->state = ED25519_OK; self->secret_key = NULL; self->public_keys = NULL; self->revoked_keys = NULL; #ifdef HAVE_LIBSODIUM if (sodium_init() < 0) - { - self->initialized = FALSE; - g_warning ("libsodium library couldn't be initialized"); - } + self->state = ED25519_FAILED_INITIALIZATION; #else - g_error ("ed25519 signature isn't supported"); + self->state = ED25519_NOT_SUPPORTED; #endif /* HAVE_LIBSODIUM */ } +static gboolean +_ostree_sign_ed25519_is_initialized (OstreeSignEd25519 *self, GError **error) +{ + switch (self->state) + { + case ED25519_OK: + break; + case ED25519_NOT_SUPPORTED: + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "ed25519: engine is not supported"); + return FALSE; + case ED25519_FAILED_INITIALIZATION: + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "ed25519: libsodium library isn't initialized properly"); + return FALSE; + } + + return TRUE; +} + gboolean ostree_sign_ed25519_data (OstreeSign *self, GBytes *data, GBytes **signature, @@ -117,10 +141,13 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, guchar *sig = NULL; #endif - if ((sign->initialized != TRUE) || (sign->secret_key == NULL)) + if (!_ostree_sign_ed25519_is_initialized (sign, error)) + goto err; + + if (sign->secret_key == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Not able to sign: libsodium library isn't initialized properly"); + "secret key is not set"); goto err; } #ifdef HAVE_LIBSODIUM @@ -135,7 +162,7 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, sign->secret_key)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Not able to sign the object"); + "fail to sign the object"); goto err; } @@ -143,6 +170,7 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, return TRUE; #endif /* HAVE_LIBSODIUM */ err: + g_prefix_error (error, "Not able to sign: "); return FALSE; } @@ -165,11 +193,14 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + if (!_ostree_sign_ed25519_is_initialized (sign, error)) + goto out; + if (signatures == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "signature: ed25519: commit have no signatures of my type"); + "ed25519: commit have no signatures of my type"); goto out; } @@ -177,14 +208,7 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "signature: ed25519: wrong type passed for verification"); - goto out; - } - - if (sign->initialized != TRUE) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Not able to verify: libsodium library isn't initialized properly"); + "ed25519: wrong type passed for verification"); goto out; } @@ -248,10 +272,12 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, if (ret != TRUE) g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Not able to verify: no valid signatures found"); + "no valid signatures found"); #endif /* HAVE_LIBSODIUM */ out: + if (ret != TRUE) + g_prefix_error (error, "Not able to verify: "); return ret; } @@ -283,9 +309,12 @@ gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); -#ifdef HAVE_LIBSODIUM OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + if (!_ostree_sign_ed25519_is_initialized (sign, error)) + goto err; + +#ifdef HAVE_LIBSODIUM /* Clear secret key */ if (sign->secret_key != NULL) { @@ -309,8 +338,9 @@ gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, } return TRUE; - #endif /* HAVE_LIBSODIUM */ + +err: return FALSE; } @@ -325,11 +355,13 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + + if (!ostree_sign_ed25519_clear_keys (self, error)) + goto err; + #ifdef HAVE_LIBSODIUM OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); - ostree_sign_ed25519_clear_keys (self, error); - gsize n_elements = 0; if (g_variant_is_of_type (secret_key, G_VARIANT_TYPE_STRING)) @@ -357,9 +389,9 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, } return TRUE; +#endif /* HAVE_LIBSODIUM */ err: -#endif /* HAVE_LIBSODIUM */ return FALSE; } @@ -374,7 +406,8 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - ostree_sign_ed25519_clear_keys (self, error); + if (!ostree_sign_ed25519_clear_keys (self, error)) + return FALSE; return ostree_sign_ed25519_add_pk (self, public_key, error); } @@ -390,11 +423,14 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); -#ifdef HAVE_LIBSODIUM OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + + if (!_ostree_sign_ed25519_is_initialized (sign, error)) + goto err; + +#ifdef HAVE_LIBSODIUM g_autofree char * hex = NULL; gpointer key = NULL; - gsize n_elements = 0; if (g_variant_is_of_type (public_key, G_VARIANT_TYPE_STRING)) @@ -430,9 +466,9 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, } return TRUE; +#endif /* HAVE_LIBSODIUM */ err: -#endif /* HAVE_LIBSODIUM */ return FALSE; } @@ -679,6 +715,10 @@ ostree_sign_ed25519_load_pk (OstreeSign *self, const gchar *filename = NULL; + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); + if (!_ostree_sign_ed25519_is_initialized (sign, error)) + return FALSE; + /* Read keys only from single file provided */ if (g_variant_lookup (options, "filename", "&s", &filename)) return _load_pk_from_file (self, filename, TRUE, error); From 194ab368f2425bf2bd1d9d721927696b93eaf933 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 6 Dec 2019 17:18:04 +0300 Subject: [PATCH 62/75] lib/repo-pull: return error from signing engine Return the collected errors from signing engines in case if verification failed for the commit. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 0c33b0f6..7bbb1acd 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -2005,7 +2005,7 @@ scan_commit_object (OtPullData *pull_data, gboolean ret = FALSE; /* list all signature types in detached metadata and check if signed by any? */ g_auto (GStrv) names = ostree_sign_list_names(); - for (char **iter=names; iter && *iter; iter++) + for (char **iter=names; !ret && iter && *iter; iter++) { g_autoptr (OstreeSign) sign = NULL; g_autoptr (GError) local_error = NULL; @@ -2023,15 +2023,28 @@ scan_commit_object (OtPullData *pull_data, checksum, cancellable, &local_error)) - ret = TRUE; + { + ret = TRUE; + g_clear_error (error); + } + + /* Save error message for better reason detection later if needed */ + if (!ret) + { + if (*error == NULL) + g_propagate_error (error, g_steal_pointer (&local_error)); + else + g_prefix_error (error, "%s; ", local_error->message); + } + } - } if (!ret) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't verify commit"); + g_prefix_error (error, "Can't verify commit %s: ", checksum); return FALSE; } + /* Clear non-fatal error */ + g_clear_error (error); } /* If we found a legacy transaction flag, assume we have to scan. From 59b9e64b724e3e9337e8f47fe8f99448cd7086f7 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Sat, 7 Dec 2019 19:28:41 +0300 Subject: [PATCH 63/75] lib/repo-pull: return errors from signature engines Improve error handling for signatures checks -- passthrough real reasons from signature engines instead of using common messages. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 121 ++++++++++++++++++------------- 1 file changed, 70 insertions(+), 51 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 7bbb1acd..08534398 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1485,14 +1485,14 @@ process_verify_result (OtPullData *pull_data, static gboolean _load_public_keys (OstreeSign *sign, OstreeRepo *repo, - const gchar *remote_name) + const gchar *remote_name, + GError **error) { g_autofree gchar *pk_ascii = NULL; g_autofree gchar *pk_file = NULL; gboolean loaded_from_file = TRUE; gboolean loaded_inlined = TRUE; - g_autoptr (GError) error = NULL; ostree_repo_get_remote_option (repo, remote_name, @@ -1528,48 +1528,53 @@ _load_public_keys (OstreeSign *sign, g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); options = g_variant_builder_end (builder); - if (ostree_sign_load_pk (sign, options, &error)) + if (ostree_sign_load_pk (sign, options, error)) loaded_from_file = TRUE; else - { - g_assert (error); g_debug("Unable to load public keys for '%s' from file '%s': %s", - ostree_sign_get_name(sign), pk_file, error->message); - g_clear_error (&error); - } + ostree_sign_get_name(sign), pk_file, (*error)->message); } if (pk_ascii != NULL) { + g_autoptr (GError) local_error = NULL; g_autoptr (GVariant) pk = g_variant_new_string(pk_ascii); /* Add inlined public key */ if (loaded_from_file) - loaded_inlined = ostree_sign_add_pk (sign, pk, &error); + loaded_inlined = ostree_sign_add_pk (sign, pk, &local_error); else - loaded_inlined = ostree_sign_set_pk (sign, pk, &error); + loaded_inlined = ostree_sign_set_pk (sign, pk, &local_error); if (!loaded_inlined) { - if (error == NULL) - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, - "unknown reason"); - g_debug("Unable to load public key '%s' for '%s': %s", - pk_ascii, ostree_sign_get_name(sign), error->message); - g_clear_error (&error); + pk_ascii, ostree_sign_get_name(sign), local_error->message); + + /* Save error message for better reason detection later if needed */ + if (*error == NULL) + g_propagate_error (error, g_steal_pointer (&local_error)); + else + g_prefix_error (error, "%s; ", local_error->message); } } /* Return true if able to load from any source */ - return (loaded_from_file || loaded_inlined); + if (loaded_from_file || loaded_inlined) + { + g_clear_error (error); + return TRUE; + } + + return FALSE; } static gboolean _ostree_repo_sign_verify (OstreeRepo *repo, const gchar *remote_name, GBytes *signed_data, - GVariant *metadata) + GVariant *metadata, + GError **error) { /* list all signature types in detached metadata and check if signed by any? */ g_auto (GStrv) names = ostree_sign_list_names(); @@ -1596,18 +1601,33 @@ _ostree_repo_sign_verify (OstreeRepo *repo, continue; /* Try to load public key(s) according remote's configuration */ - if (!_load_public_keys (sign, repo, remote_name)) - continue; + if (_load_public_keys (sign, repo, remote_name, &local_error)) + { + /* Return true if any signature fit to pre-loaded public keys. + * If no keys configured -- then system configuration will be used */ + if (ostree_sign_data_verify (sign, + signed_data, + signatures, + &local_error)) + { + g_clear_error (error); + return TRUE; + } + } - /* Return true if any signature fit to pre-loaded public keys. - * If no keys configured -- then system configuration will be used */ - if (ostree_sign_data_verify (sign, - signed_data, - signatures, - &local_error)) - return TRUE; + /* Save error message for better reason detection later if needed */ + if (*error == NULL) + g_propagate_error (error, g_steal_pointer (&local_error)); + else + g_prefix_error (error, "%s; ", local_error->message); } + /* In case if there were no signatures of known type + * or metadata contains invalid data */ + if (*error == NULL) + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Metadata doesn't signed with supported type"); + return FALSE; } @@ -1658,10 +1678,9 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, return FALSE; } - if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, signed_data, detached_metadata)) + if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, signed_data, detached_metadata, error)) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't verify commit"); + g_prefix_error (error, "Can't verify commit"); return FALSE; } @@ -2014,18 +2033,19 @@ scan_commit_object (OtPullData *pull_data, continue; /* Try to load public key(s) according remote's configuration */ - if (!_load_public_keys (sign, pull_data->repo, pull_data->remote_name)) - continue; - - /* Set return to true if any sign fit */ - if (ostree_sign_commit_verify (sign, - pull_data->repo, - checksum, - cancellable, - &local_error)) + if (_load_public_keys (sign, pull_data->repo, pull_data->remote_name, &local_error)) { - ret = TRUE; - g_clear_error (error); + + /* Set return to true if any sign fit */ + if (ostree_sign_commit_verify (sign, + pull_data->repo, + checksum, + cancellable, + &local_error)) + { + ret = TRUE; + g_clear_error (error); + } } /* Save error message for better reason detection later if needed */ @@ -4416,12 +4436,13 @@ ostree_repo_pull_with_options (OstreeRepo *self, if (bytes_summary && bytes_sig) { g_autoptr(GVariant) signatures = NULL; + g_autoptr(GError) temp_error = NULL; signatures = g_variant_new_from_bytes (OSTREE_SUMMARY_SIG_GVARIANT_FORMAT, bytes_sig, FALSE); - if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures)) + if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures, &temp_error)) { gboolean ret = FALSE; @@ -4452,14 +4473,16 @@ ostree_repo_pull_with_options (OstreeRepo *self, cancellable, error)) goto out; - if (_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures)) + if (_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures, error)) ret = TRUE; } + else + g_propagate_error (error, g_steal_pointer (&temp_error)); + if (!ret) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't verify summary"); + g_prefix_error (error, "Can't verify summary: "); goto out; } } @@ -6560,12 +6583,8 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, sig_variant = g_variant_new_from_bytes (OSTREE_SUMMARY_SIG_GVARIANT_FORMAT, signatures, FALSE); - if (!_ostree_repo_sign_verify (self, name, summary, sig_variant)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "Signature verification enabled, but no valid signatures found"); - goto out; - } + if (!_ostree_repo_sign_verify (self, name, summary, sig_variant, error)) + goto out; } } From fbc5927d7ef4ab552efe94abc4e0e69a113ba0e7 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 29 Jan 2020 14:09:00 +0000 Subject: [PATCH 64/75] build-sys: Print libsodium status at end of configure Like we do with other features. --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index e41ccb70..5d9d2a23 100644 --- a/configure.ac +++ b/configure.ac @@ -641,6 +641,7 @@ echo " cryptographic checksums: $with_crypto systemd: $with_libsystemd libmount: $with_libmount + libsodium (ed25519 signatures): $with_libsodium libarchive (parse tar files directly): $with_libarchive static deltas: yes (always enabled now) O_TMPFILE: $enable_otmpfile From 2a0edccbd346fccb3f40102d4c7bfa3f22a14feb Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 29 Jan 2020 14:17:19 +0000 Subject: [PATCH 65/75] sign-ed25519: Convert some functions to new style The "new style" code generally avoids `goto err` because it conflicts with `__attribute__((cleanup))`. This fixes a compiler warning. --- src/libostree/ostree-sign-ed25519.c | 48 ++++++++--------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 6bf0b10b..2941a9fe 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -426,10 +426,9 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); if (!_ostree_sign_ed25519_is_initialized (sign, error)) - goto err; + return FALSE; #ifdef HAVE_LIBSODIUM - g_autofree char * hex = NULL; gpointer key = NULL; gsize n_elements = 0; @@ -444,20 +443,14 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, } else { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unknown ed25519 public key type"); - goto err; + return glnx_throw (error, "Unknown ed25519 public key type"); } - hex = g_malloc0 (crypto_sign_PUBLICKEYBYTES*2 + 1); + g_autofree char *hex = g_malloc0 (crypto_sign_PUBLICKEYBYTES*2 + 1); g_debug ("Read ed25519 public 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 public key"); - goto err; - } + return glnx_throw (error, "Incorrect ed25519 public key"); if (g_list_find_custom (sign->public_keys, key, _compare_ed25519_keys) == NULL) { @@ -465,11 +458,8 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, sign->public_keys = g_list_prepend (sign->public_keys, newkey); } - return TRUE; #endif /* HAVE_LIBSODIUM */ - -err: - return FALSE; + return TRUE; } #ifdef HAVE_LIBSODIUM @@ -482,32 +472,21 @@ _ed25519_add_revoked (OstreeSign *self, g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); + if (!g_variant_is_of_type (revoked_key, G_VARIANT_TYPE_STRING)) + return glnx_throw (error, "Unknown ed25519 revoked key type"); + OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); - g_autofree char * hex = NULL; - gpointer key = NULL; + const gchar *rk_ascii = g_variant_get_string (revoked_key, NULL); gsize n_elements = 0; + gpointer key = g_base64_decode (rk_ascii, &n_elements); - 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_autofree char * 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; + return glnx_throw (error, "Incorrect ed25519 revoked key"); } if (g_list_find_custom (sign->revoked_keys, key, _compare_ed25519_keys) == NULL) @@ -517,9 +496,6 @@ _ed25519_add_revoked (OstreeSign *self, } return TRUE; - -err: - return FALSE; } #endif /* HAVE_LIBSODIUM */ From e2bd2abc67f1a4781c866aa1e69e6b7a447a5aee Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 14 Feb 2020 14:54:00 +0000 Subject: [PATCH 66/75] sign-dummy: Convert to current code style This keeps the code style consistent. --- src/libostree/ostree-sign-dummy.c | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index df5a7b82..ef2caa9f 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -160,25 +160,13 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); - gboolean ret = FALSE; - if (signatures == NULL) - { - g_set_error_literal (error, - G_IO_ERROR, G_IO_ERROR_FAILED, - "signature: dummy: commit have no signatures of my type"); - goto err; - } - + return glnx_throw (error, "signature: dummy: commit have no signatures of my type"); if (!g_variant_is_of_type (signatures, (GVariantType *) OSTREE_SIGN_METADATA_DUMMY_TYPE)) - { - g_set_error_literal (error, - G_IO_ERROR, G_IO_ERROR_FAILED, - "signature: dummy: wrong type passed for verification"); - goto err; - } + return glnx_throw (error, "signature: dummy: wrong type passed for verification"); + gboolean verified = FALSE; for (gsize i = 0; i < g_variant_n_children(signatures); i++) { g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i); @@ -191,13 +179,12 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, g_debug("Stored signature %d: %s", (gint)i, sign->pk_ascii); if (!g_strcmp0(sign_ascii, sign->pk_ascii)) - ret = TRUE; + verified = TRUE; + else + return glnx_throw (error, "signature: dummy: incorrect signature %" G_GSIZE_FORMAT, i); } - if (ret == FALSE && *error == NULL) - g_set_error_literal (error, - G_IO_ERROR, G_IO_ERROR_FAILED, - "signature: dummy: incorrect signature"); + if (!verified) + return glnx_throw (error, "signature: dummy: no signatures"); -err: - return ret; + return TRUE; } From 9d02199675a5fff44716854fbbe5bd9874d0c6f9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 14 Feb 2020 15:52:48 +0000 Subject: [PATCH 67/75] signing: Remove g_debug(__FUNCTION__) This type of thing is better done via `gdb` and/or userspace tracing (systemtap/bpftrace etc.) --- src/libostree/ostree-sign-dummy.c | 10 ---------- src/libostree/ostree-sign-ed25519.c | 17 ----------------- src/libostree/ostree-sign.c | 16 ---------------- 3 files changed, 43 deletions(-) diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index ef2caa9f..b9d8abf4 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -56,7 +56,6 @@ G_DEFINE_TYPE_WITH_CODE (OstreeSignDummy, _ostree_sign_dummy, G_TYPE_OBJECT, static void ostree_sign_dummy_iface_init (OstreeSignInterface *self) { - g_debug ("%s enter", __FUNCTION__); self->get_name = ostree_sign_dummy_get_name; self->data = ostree_sign_dummy_data; @@ -72,13 +71,11 @@ ostree_sign_dummy_iface_init (OstreeSignInterface *self) static void _ostree_sign_dummy_class_init (OstreeSignDummyClass *self) { - g_debug ("%s enter", __FUNCTION__); } static void _ostree_sign_dummy_init (OstreeSignDummy *self) { - g_debug ("%s enter", __FUNCTION__); self->sk_ascii = NULL; self->pk_ascii = NULL; @@ -86,7 +83,6 @@ _ostree_sign_dummy_init (OstreeSignDummy *self) gboolean ostree_sign_dummy_set_sk (OstreeSign *self, GVariant *key, GError **error) { - g_debug ("%s enter", __FUNCTION__); OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); @@ -99,7 +95,6 @@ gboolean ostree_sign_dummy_set_sk (OstreeSign *self, GVariant *key, GError **err gboolean ostree_sign_dummy_set_pk (OstreeSign *self, GVariant *key, GError **error) { - g_debug ("%s enter", __FUNCTION__); OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); @@ -117,7 +112,6 @@ gboolean ostree_sign_dummy_data (OstreeSign *self, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); OstreeSignDummy *sign = _ostree_sign_dummy_get_instance_private(OSTREE_SIGN_DUMMY(self)); @@ -129,7 +123,6 @@ gboolean ostree_sign_dummy_data (OstreeSign *self, const gchar * ostree_sign_dummy_get_name (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); return OSTREE_SIGN_DUMMY_NAME; @@ -137,14 +130,12 @@ const gchar * ostree_sign_dummy_get_name (OstreeSign *self) const gchar * ostree_sign_dummy_metadata_key (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); return OSTREE_SIGN_METADATA_DUMMY_KEY; } const gchar * ostree_sign_dummy_metadata_format (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); return OSTREE_SIGN_METADATA_DUMMY_TYPE; } @@ -154,7 +145,6 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, GVariant *signatures, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (data != NULL, FALSE); diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 2941a9fe..ce656678 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -68,7 +68,6 @@ G_DEFINE_TYPE_WITH_CODE (OstreeSignEd25519, _ostree_sign_ed25519, G_TYPE_OBJECT, static void ostree_sign_ed25519_iface_init (OstreeSignInterface *self) { - g_debug ("%s enter", __FUNCTION__); self->data = ostree_sign_ed25519_data; self->data_verify = ostree_sign_ed25519_data_verify; @@ -85,13 +84,11 @@ ostree_sign_ed25519_iface_init (OstreeSignInterface *self) static void _ostree_sign_ed25519_class_init (OstreeSignEd25519Class *self) { - g_debug ("%s enter", __FUNCTION__); } static void _ostree_sign_ed25519_init (OstreeSignEd25519 *self) { - g_debug ("%s enter", __FUNCTION__); self->state = ED25519_OK; self->secret_key = NULL; @@ -133,7 +130,6 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, 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)); @@ -186,7 +182,6 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, GVariant *signatures, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (data != NULL, FALSE); gboolean ret = FALSE; @@ -283,7 +278,6 @@ out: const gchar * ostree_sign_ed25519_get_name (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); return OSTREE_SIGN_ED25519_NAME; @@ -291,14 +285,12 @@ const gchar * ostree_sign_ed25519_get_name (OstreeSign *self) const gchar * ostree_sign_ed25519_metadata_key (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); return OSTREE_SIGN_METADATA_ED25519_KEY; } const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); return OSTREE_SIGN_METADATA_ED25519_TYPE; } @@ -306,7 +298,6 @@ const gchar * ostree_sign_ed25519_metadata_format (OstreeSign *self) gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, 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)); @@ -352,7 +343,6 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, GVariant *secret_key, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); @@ -403,7 +393,6 @@ gboolean ostree_sign_ed25519_set_pk (OstreeSign *self, GVariant *public_key, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); if (!ostree_sign_ed25519_clear_keys (self, error)) @@ -420,7 +409,6 @@ gboolean ostree_sign_ed25519_add_pk (OstreeSign *self, GVariant *public_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)); @@ -469,7 +457,6 @@ _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); if (!g_variant_is_of_type (revoked_key, G_VARIANT_TYPE_STRING)) @@ -506,7 +493,6 @@ _load_pk_from_stream (OstreeSign *self, gboolean trusted, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (key_data_in, FALSE); #ifdef HAVE_LIBSODIUM gboolean ret = FALSE; @@ -558,7 +544,6 @@ _load_pk_from_file (OstreeSign *self, gboolean trusted, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_debug ("Processing file '%s'", filename); g_autoptr (GFile) keyfile = NULL; @@ -602,7 +587,6 @@ _ed25519_load_pk (OstreeSign *self, gboolean trusted, GError **error) { - g_debug ("%s enter", __FUNCTION__); gboolean ret = FALSE; const gchar *custom_dir = NULL; @@ -687,7 +671,6 @@ ostree_sign_ed25519_load_pk (OstreeSign *self, GVariant *options, GError **error) { - g_debug ("%s enter", __FUNCTION__); const gchar *filename = NULL; diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index e7962425..23ec84ee 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -98,7 +98,6 @@ ostree_sign_default_init (OstreeSignInterface *iface) const gchar * ostree_sign_metadata_key (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->metadata_key != NULL, NULL); return OSTREE_SIGN_GET_IFACE (self)->metadata_key (self); @@ -119,7 +118,6 @@ ostree_sign_metadata_key (OstreeSign *self) const gchar * ostree_sign_metadata_format (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->metadata_format != NULL, NULL); return OSTREE_SIGN_GET_IFACE (self)->metadata_format (self); @@ -140,7 +138,6 @@ gboolean ostree_sign_clear_keys (OstreeSign *self, GError **error) { - g_debug ("%s enter", __FUNCTION__); if (OSTREE_SIGN_GET_IFACE (self)->clear_keys == NULL) return TRUE; @@ -168,7 +165,6 @@ ostree_sign_set_sk (OstreeSign *self, GVariant *secret_key, GError **error) { - g_debug ("%s enter", __FUNCTION__); if (OSTREE_SIGN_GET_IFACE (self)->set_sk == NULL) return TRUE; @@ -197,7 +193,6 @@ ostree_sign_set_pk (OstreeSign *self, GVariant *public_key, GError **error) { - g_debug ("%s enter", __FUNCTION__); if (OSTREE_SIGN_GET_IFACE (self)->set_pk == NULL) return TRUE; @@ -226,7 +221,6 @@ ostree_sign_add_pk (OstreeSign *self, GVariant *public_key, GError **error) { - g_debug ("%s enter", __FUNCTION__); if (OSTREE_SIGN_GET_IFACE (self)->add_pk == NULL) return TRUE; @@ -266,7 +260,6 @@ ostree_sign_load_pk (OstreeSign *self, GVariant *options, GError **error) { - g_debug ("%s enter", __FUNCTION__); if (OSTREE_SIGN_GET_IFACE (self)->load_pk == NULL) return TRUE; @@ -300,7 +293,6 @@ ostree_sign_data (OstreeSign *self, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->data != NULL, FALSE); @@ -331,7 +323,6 @@ ostree_sign_data_verify (OstreeSign *self, GVariant *signatures, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->data_verify != NULL, FALSE); @@ -346,7 +337,6 @@ _sign_detached_metadata_append (OstreeSign *self, GVariant *existing_metadata, GBytes *signature_bytes) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (signature_bytes != NULL, FALSE); GVariantDict metadata_dict; @@ -401,7 +391,6 @@ ostree_sign_commit_verify (OstreeSign *self, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_autoptr(GVariant) commit_variant = NULL; @@ -455,7 +444,6 @@ ostree_sign_commit_verify (OstreeSign *self, const gchar * ostree_sign_get_name (OstreeSign *self) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), NULL); g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->get_name != NULL, NULL); @@ -487,7 +475,6 @@ ostree_sign_commit (OstreeSign *self, GCancellable *cancellable, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_autoptr(GBytes) commit_data = NULL; g_autoptr(GBytes) signature = NULL; @@ -537,7 +524,6 @@ ostree_sign_commit (OstreeSign *self, GStrv ostree_sign_list_names(void) { - g_debug ("%s enter", __FUNCTION__); GStrv names = g_new0 (char *, G_N_ELEMENTS(sign_types) + 1); gint i = 0; @@ -567,7 +553,6 @@ ostree_sign_list_names(void) OstreeSign * ostree_sign_get_by_name (const gchar *name, GError **error) { - g_debug ("%s enter", __FUNCTION__); OstreeSign *sign = NULL; @@ -616,7 +601,6 @@ ostree_sign_summary (OstreeSign *self, GCancellable *cancellable, GError **error) { - g_debug ("%s enter", __FUNCTION__); g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); From 09d5b475afdb19000203ec090b8077b7cfc4eb44 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Fri, 31 Jan 2020 13:00:59 +0300 Subject: [PATCH 68/75] tests/sign: added check with file and single key on pull Additional test of signatures check behavior during the pull with keys file containing wrong signatures and correct verification key. Both are set as a part of remote's configuration. Signed-off-by: Denis Pynkin --- tests/test-signed-pull.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh index 4d8d7aab..e9d1bd75 100755 --- a/tests/test-signed-pull.sh +++ b/tests/test-signed-pull.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..6" +echo "1..8" setup_fake_remote_repo1 "archive" @@ -73,6 +73,8 @@ test_signed_pull "dummy" "" if ! has_libsodium; then echo "ok ed25519-key pull signed commit # SKIP due libsodium unavailability" echo "ok ed25519-key re-pull signature for stored commit # SKIP due libsodium unavailability" + echo "ok ed25519-key+file pull signed commit # SKIP due libsodium unavailability" + echo "ok ed25519-key+file re-pull signature for stored commit # SKIP due libsodium unavailability" echo "ok ed25519-file pull signed commit # SKIP due libsodium unavailability" echo "ok ed25519-file re-pull signature for stored commit # SKIP due libsodium unavailability" exit 0 @@ -98,6 +100,11 @@ for((i=0;i<100;i++)); do # Generate a list with some public signatures gen_ed25519_random_public done > ${PUBKEYS} + +# Test case with the file containing incorrect signatures and with the correct key set +${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-file "${PUBKEYS}" +test_signed_pull "ed25519" "key+file" + # Add correct key into the list echo ${PUBLIC} >> ${PUBKEYS} From aaf73f6afca4c72ef9c7be6f64507babe7130a1b Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 18 Feb 2020 00:11:38 +0300 Subject: [PATCH 69/75] sign-ed25519: Convert functions to new style The "new style" code generally avoids `goto err` because it conflicts with `__attribute__((cleanup))`. This fixes a compiler warning. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-ed25519.c | 118 ++++++++-------------------- 1 file changed, 34 insertions(+), 84 deletions(-) diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index ce656678..8df61aed 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -111,13 +111,9 @@ _ostree_sign_ed25519_is_initialized (OstreeSignEd25519 *self, GError **error) case ED25519_OK: break; case ED25519_NOT_SUPPORTED: - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "ed25519: engine is not supported"); - return FALSE; + return glnx_throw(error, "ed25519: engine is not supported"); case ED25519_FAILED_INITIALIZATION: - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "ed25519: libsodium library isn't initialized properly"); - return FALSE; + return glnx_throw(error, "ed25519: libsodium library isn't initialized properly"); } return TRUE; @@ -138,14 +134,11 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, #endif if (!_ostree_sign_ed25519_is_initialized (sign, error)) - goto err; + return FALSE; if (sign->secret_key == NULL) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "secret key is not set"); - goto err; - } + return glnx_throw (error, "Not able to sign: secret key is not set"); + #ifdef HAVE_LIBSODIUM unsigned long long sig_size = 0; @@ -157,16 +150,12 @@ gboolean ostree_sign_ed25519_data (OstreeSign *self, g_bytes_get_size (data), sign->secret_key)) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "fail to sign the object"); - goto err; + return glnx_throw (error, "Not able to sign: fail to sign the object"); } *signature = g_bytes_new_take (sig, sig_size); return TRUE; #endif /* HAVE_LIBSODIUM */ -err: - g_prefix_error (error, "Not able to sign: "); return FALSE; } @@ -184,28 +173,17 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, { g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (data != NULL, FALSE); - gboolean ret = FALSE; OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); if (!_ostree_sign_ed25519_is_initialized (sign, error)) - goto out; + return FALSE; if (signatures == NULL) - { - g_set_error_literal (error, - G_IO_ERROR, G_IO_ERROR_FAILED, - "ed25519: commit have no signatures of my type"); - goto out; - } + return glnx_throw (error, "ed25519: commit have no signatures of my type"); if (!g_variant_is_of_type (signatures, (GVariantType *) OSTREE_SIGN_METADATA_ED25519_TYPE)) - { - g_set_error_literal (error, - G_IO_ERROR, G_IO_ERROR_FAILED, - "ed25519: wrong type passed for verification"); - goto out; - } + return glnx_throw (error, "ed25519: wrong type passed for verification"); #ifdef HAVE_LIBSODIUM /* If no keys pre-loaded then, @@ -219,7 +197,7 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, options = g_variant_builder_end (builder); if (!ostree_sign_ed25519_load_pk (self, options, error)) - goto out; + return FALSE; } g_debug ("verify: data hash = 0x%x", g_bytes_hash(data)); @@ -257,23 +235,17 @@ gboolean ostree_sign_ed25519_data_verify (OstreeSign *self, } else { - ret = TRUE; g_debug ("Signature verified successfully with key '%s'", sodium_bin2hex (hex, crypto_sign_PUBLICKEYBYTES*2+1, public_key->data, crypto_sign_PUBLICKEYBYTES)); - break; + return TRUE; } } } - if (ret != TRUE) - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "no valid signatures found"); + return glnx_throw (error, "Not able to verify: no valid signatures found"); #endif /* HAVE_LIBSODIUM */ -out: - if (ret != TRUE) - g_prefix_error (error, "Not able to verify: "); - return ret; + return FALSE; } const gchar * ostree_sign_ed25519_get_name (OstreeSign *self) @@ -303,7 +275,7 @@ gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); if (!_ostree_sign_ed25519_is_initialized (sign, error)) - goto err; + return FALSE; #ifdef HAVE_LIBSODIUM /* Clear secret key */ @@ -331,7 +303,6 @@ gboolean ostree_sign_ed25519_clear_keys (OstreeSign *self, return TRUE; #endif /* HAVE_LIBSODIUM */ -err: return FALSE; } @@ -347,7 +318,7 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, if (!ostree_sign_ed25519_clear_keys (self, error)) - goto err; + return FALSE; #ifdef HAVE_LIBSODIUM OstreeSignEd25519 *sign = _ostree_sign_ed25519_get_instance_private(OSTREE_SIGN_ED25519(self)); @@ -365,23 +336,15 @@ gboolean ostree_sign_ed25519_set_sk (OstreeSign *self, } else { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unknown ed25519 secret key type"); - goto err; + return glnx_throw (error, "Unknown ed25519 secret key type"); } - if (n_elements != crypto_sign_SECRETKEYBYTES) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Incorrect ed25519 secret key"); - goto err; - } + return glnx_throw (error, "Incorrect ed25519 secret key"); return TRUE; #endif /* HAVE_LIBSODIUM */ -err: return FALSE; } @@ -506,10 +469,10 @@ _load_pk_from_stream (OstreeSign *self, gboolean added = FALSE; if (*error != NULL) - goto err; + return FALSE; if (line == NULL) - goto out; + return ret; /* Read the key itself */ /* base64 encoded key */ @@ -529,11 +492,6 @@ _load_pk_from_stream (OstreeSign *self, if (added) ret = TRUE; } - -out: - return ret; - -err: #endif /* HAVE_LIBSODIUM */ return FALSE; } @@ -553,15 +511,13 @@ _load_pk_from_file (OstreeSign *self, if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { g_debug ("Can't open file '%s' with public keys", filename); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "File object '%s' is not a regular file", filename); - goto err; + return glnx_throw (error, "File object '%s' is not a regular file", filename); } keyfile = g_file_new_for_path (filename); key_stream_in = g_file_read (keyfile, NULL, error); if (key_stream_in == NULL) - goto err; + return FALSE; key_data_in = g_data_input_stream_new (G_INPUT_STREAM(key_stream_in)); g_assert (key_data_in != NULL); @@ -569,16 +525,12 @@ _load_pk_from_file (OstreeSign *self, 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 glnx_throw (error, + "signature: ed25519: no valid keys in file '%s'", + filename); } return TRUE; -err: - return FALSE; } static gboolean @@ -639,21 +591,19 @@ _ed25519_load_pk (OstreeSign *self, /* 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 (!_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 glnx_throw (error, "signature: ed25519: no keys loaded"); return ret; } From 1e3bdef2851260b8ac3503744d4157d527e3823d Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 18 Feb 2020 00:46:51 +0300 Subject: [PATCH 70/75] sign-dummy: optimize ostree_sign_dummy_data_verify Return TRUE as soon as any signature verified. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign-dummy.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libostree/ostree-sign-dummy.c b/src/libostree/ostree-sign-dummy.c index b9d8abf4..722d461b 100644 --- a/src/libostree/ostree-sign-dummy.c +++ b/src/libostree/ostree-sign-dummy.c @@ -156,7 +156,6 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, if (!g_variant_is_of_type (signatures, (GVariantType *) OSTREE_SIGN_METADATA_DUMMY_TYPE)) return glnx_throw (error, "signature: dummy: wrong type passed for verification"); - gboolean verified = FALSE; for (gsize i = 0; i < g_variant_n_children(signatures); i++) { g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i); @@ -169,12 +168,10 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self, g_debug("Stored signature %d: %s", (gint)i, sign->pk_ascii); if (!g_strcmp0(sign_ascii, sign->pk_ascii)) - verified = TRUE; + return TRUE; else return glnx_throw (error, "signature: dummy: incorrect signature %" G_GSIZE_FORMAT, i); } - if (!verified) - return glnx_throw (error, "signature: dummy: no signatures"); - return TRUE; + return glnx_throw (error, "signature: dummy: no signatures"); } From e16faa58f4c3be33f073b8986cd628f18047dabd Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Tue, 18 Feb 2020 00:50:21 +0300 Subject: [PATCH 71/75] lib/sign: convert ostree_sign_summary to new style The "new style" code generally avoids `goto err` because it conflicts with `__attribute__((cleanup))`. This fixes a compiler warning. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index 23ec84ee..c96f751f 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -604,37 +604,32 @@ ostree_sign_summary (OstreeSign *self, g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); - gboolean ret = FALSE; - g_autoptr(GVariant) normalized = NULL; g_autoptr(GBytes) summary_data = NULL; g_autoptr(GVariant) metadata = NULL; glnx_autofd int fd = -1; if (!glnx_openat_rdonly (repo->repo_dir_fd, "summary", TRUE, &fd, error)) - goto out; + return FALSE; summary_data = ot_fd_readall_or_mmap (fd, 0, error); if (!summary_data) - goto out; + return FALSE; /* Note that fd is reused below */ glnx_close_fd (&fd); if (!ot_openat_ignore_enoent (repo->repo_dir_fd, "summary.sig", &fd, error)) - goto out; + return FALSE; + if (fd >= 0) { if (!ot_variant_read_fd (fd, 0, OSTREE_SUMMARY_SIG_GVARIANT_FORMAT, FALSE, &metadata, error)) - goto out; + return FALSE; } if (g_variant_n_children(keys) == 0) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No keys passed for signing summary"); - goto out; - } + return glnx_throw (error, "No keys passed for signing summary"); GVariantIter *iter; GVariant *key; @@ -645,14 +640,14 @@ ostree_sign_summary (OstreeSign *self, g_autoptr (GBytes) signature = NULL; if (!ostree_sign_set_sk (self, key, error)) - goto out; + return FALSE; if (!ostree_sign_data (self, summary_data, &signature, cancellable, error)) - goto out; + return FALSE; g_autoptr(GVariant) old_metadata = g_steal_pointer (&metadata); metadata = @@ -667,10 +662,7 @@ ostree_sign_summary (OstreeSign *self, g_variant_get_data (normalized), g_variant_get_size (normalized), cancellable, error)) - goto out; + return FALSE; - ret = TRUE; - -out: - return ret; + return TRUE; } From 5a39281fbec6956e16ef17f33288f6d6b7ceb205 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Thu, 20 Feb 2020 02:43:36 +0300 Subject: [PATCH 72/75] tests/sign: check pull failure with invalid remote options Pull should to fail if no known signature available in remote's configuration or well-known places. Signed-off-by: Denis Pynkin --- tests/test-signed-pull.sh | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/test-signed-pull.sh b/tests/test-signed-pull.sh index e9d1bd75..f222db4f 100755 --- a/tests/test-signed-pull.sh +++ b/tests/test-signed-pull.sh @@ -23,7 +23,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..8" +echo "1..11" setup_fake_remote_repo1 "archive" @@ -67,6 +67,31 @@ function test_signed_pull() { DUMMYSIGN="dummysign" COMMIT_ARGS="--sign=${DUMMYSIGN} --sign-type=dummy" repo_init --set=sign-verify=true + +# Check if verification-key and verification-file options throw error with wrong keys +cd ${test_tmpdir} +${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo commit ${COMMIT_ARGS} \ + -b main -s "A signed commit" --tree=ref=main +${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u +if ${CMD_PREFIX} ostree --repo=repo pull origin main; then + assert_not_reached "pull without keys unexpectedly succeeded" +fi +echo "ok pull failure without keys preloaded" + +${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-key "somewrongkey" +if ${CMD_PREFIX} ostree --repo=repo pull origin main; then + assert_not_reached "pull with unknown key unexpectedly succeeded" +fi +echo "ok pull failure with incorrect key option" + +${CMD_PREFIX} ostree --repo=repo config unset 'remote "origin"'.verification-key +${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-file "/non/existing/file" +if ${CMD_PREFIX} ostree --repo=repo pull origin main; then + assert_not_reached "pull with unknown keys file unexpectedly succeeded" +fi +echo "ok pull failure with incorrect keys file option" + +# Test with correct dummy key ${CMD_PREFIX} ostree --repo=repo config set 'remote "origin"'.verification-key "${DUMMYSIGN}" test_signed_pull "dummy" "" From 584ad405491a3839d1478420bc0788077cb9e7f3 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Thu, 20 Feb 2020 03:24:14 +0300 Subject: [PATCH 73/75] lib/sign: return false for non-implemented functions Do not mask implementation anymore since we have a working engines integrated with pulling mechanism. Signed-off-by: Denis Pynkin --- src/libostree/ostree-sign.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libostree/ostree-sign.c b/src/libostree/ostree-sign.c index c96f751f..281fabc8 100644 --- a/src/libostree/ostree-sign.c +++ b/src/libostree/ostree-sign.c @@ -138,9 +138,9 @@ gboolean ostree_sign_clear_keys (OstreeSign *self, GError **error) { - + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); if (OSTREE_SIGN_GET_IFACE (self)->clear_keys == NULL) - return TRUE; + return glnx_throw (error, "not implemented"); return OSTREE_SIGN_GET_IFACE (self)->clear_keys (self, error); } @@ -165,9 +165,9 @@ ostree_sign_set_sk (OstreeSign *self, GVariant *secret_key, GError **error) { - + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); if (OSTREE_SIGN_GET_IFACE (self)->set_sk == NULL) - return TRUE; + return glnx_throw (error, "not implemented"); return OSTREE_SIGN_GET_IFACE (self)->set_sk (self, secret_key, error); } @@ -193,9 +193,9 @@ ostree_sign_set_pk (OstreeSign *self, GVariant *public_key, GError **error) { - + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); if (OSTREE_SIGN_GET_IFACE (self)->set_pk == NULL) - return TRUE; + return glnx_throw (error, "not implemented"); return OSTREE_SIGN_GET_IFACE (self)->set_pk (self, public_key, error); } @@ -221,9 +221,9 @@ ostree_sign_add_pk (OstreeSign *self, GVariant *public_key, GError **error) { - + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); if (OSTREE_SIGN_GET_IFACE (self)->add_pk == NULL) - return TRUE; + return glnx_throw (error, "not implemented"); return OSTREE_SIGN_GET_IFACE (self)->add_pk (self, public_key, error); } @@ -260,9 +260,9 @@ ostree_sign_load_pk (OstreeSign *self, GVariant *options, GError **error) { - + g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); if (OSTREE_SIGN_GET_IFACE (self)->load_pk == NULL) - return TRUE; + return glnx_throw (error, "not implemented"); return OSTREE_SIGN_GET_IFACE (self)->load_pk (self, options, error); } @@ -294,7 +294,8 @@ ostree_sign_data (OstreeSign *self, { g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->data != NULL, FALSE); + if (OSTREE_SIGN_GET_IFACE (self)->data == NULL) + return glnx_throw (error, "not implemented"); return OSTREE_SIGN_GET_IFACE (self)->data (self, data, signature, cancellable, error); } @@ -324,7 +325,8 @@ ostree_sign_data_verify (OstreeSign *self, GError **error) { g_return_val_if_fail (OSTREE_IS_SIGN (self), FALSE); - g_return_val_if_fail (OSTREE_SIGN_GET_IFACE (self)->data_verify != NULL, FALSE); + if (OSTREE_SIGN_GET_IFACE (self)->data_verify == NULL) + return glnx_throw (error, "not implemented"); return OSTREE_SIGN_GET_IFACE (self)->data_verify(self, data, signatures, error); } From cce3864160438511fe767444f682f7ab9fb510c1 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Thu, 20 Feb 2020 03:59:05 +0300 Subject: [PATCH 74/75] sign-pull: improve error handling Use glnx_* functions in signature related pull code for clear error handling. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo-pull.c | 92 +++++++++++-------------------- tests/test-signed-pull-summary.sh | 4 +- 2 files changed, 34 insertions(+), 62 deletions(-) diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 08534398..043e8dee 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -1470,7 +1470,7 @@ process_verify_result (OtPullData *pull_data, } #endif /* OSTREE_DISABLE_GPGME */ -/* _remote_load_public_keys: +/* _load_public_keys: * * Load public keys according remote's configuration: * inlined key passed via config option `verification-key` or @@ -1493,6 +1493,9 @@ _load_public_keys (OstreeSign *sign, g_autofree gchar *pk_file = NULL; gboolean loaded_from_file = TRUE; gboolean loaded_inlined = TRUE; + g_autoptr (GError) verification_error = NULL; + + glnx_throw (&verification_error, "no public keys loaded"); ostree_repo_get_remote_option (repo, remote_name, @@ -1521,6 +1524,7 @@ _load_public_keys (OstreeSign *sign, if (pk_file != NULL) { + g_autoptr (GError) local_error = NULL; g_autoptr (GVariantBuilder) builder = NULL; g_autoptr (GVariant) options = NULL; @@ -1528,11 +1532,15 @@ _load_public_keys (OstreeSign *sign, g_variant_builder_add (builder, "{sv}", "filename", g_variant_new_string (pk_file)); options = g_variant_builder_end (builder); - if (ostree_sign_load_pk (sign, options, error)) + if (ostree_sign_load_pk (sign, options, &local_error)) loaded_from_file = TRUE; else + { g_debug("Unable to load public keys for '%s' from file '%s': %s", - ostree_sign_get_name(sign), pk_file, (*error)->message); + ostree_sign_get_name(sign), pk_file, local_error->message); + /* Save error message for better reason detection later if needed */ + glnx_prefix_error (&verification_error, "%s", local_error->message); + } } if (pk_ascii != NULL) @@ -1549,24 +1557,18 @@ _load_public_keys (OstreeSign *sign, if (!loaded_inlined) { g_debug("Unable to load public key '%s' for '%s': %s", - pk_ascii, ostree_sign_get_name(sign), local_error->message); + pk_ascii, ostree_sign_get_name (sign), local_error->message); /* Save error message for better reason detection later if needed */ - if (*error == NULL) - g_propagate_error (error, g_steal_pointer (&local_error)); - else - g_prefix_error (error, "%s; ", local_error->message); + glnx_prefix_error (&verification_error, "%s", local_error->message); } } /* Return true if able to load from any source */ if (loaded_from_file || loaded_inlined) - { - g_clear_error (error); - return TRUE; - } + return TRUE; - return FALSE; + return glnx_throw (error, "%s", verification_error->message); } static gboolean @@ -1578,6 +1580,10 @@ _ostree_repo_sign_verify (OstreeRepo *repo, { /* list all signature types in detached metadata and check if signed by any? */ g_auto (GStrv) names = ostree_sign_list_names(); + g_autoptr (GError) verification_error = NULL; + + glnx_throw (&verification_error, "signed with unknown key"); + for (char **iter=names; iter && *iter; iter++) { g_autoptr (OstreeSign) sign = NULL; @@ -1609,26 +1615,16 @@ _ostree_repo_sign_verify (OstreeRepo *repo, signed_data, signatures, &local_error)) - { - g_clear_error (error); - return TRUE; - } + return TRUE; } /* Save error message for better reason detection later if needed */ - if (*error == NULL) - g_propagate_error (error, g_steal_pointer (&local_error)); - else - g_prefix_error (error, "%s; ", local_error->message); + glnx_prefix_error (&verification_error, "%s", local_error->message); } /* In case if there were no signatures of known type * or metadata contains invalid data */ - if (*error == NULL) - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "Metadata doesn't signed with supported type"); - - return FALSE; + return glnx_throw (error, "%s", verification_error->message); } static gboolean @@ -1672,17 +1668,10 @@ ostree_verify_unwritten_commit (OtPullData *pull_data, { /* Nothing to check if detached metadata is absent */ if (detached_metadata == NULL) - { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't verify commit without detached metadata"); - return FALSE; - } + return glnx_throw (error, "Can't verify commit without detached metadata"); if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, signed_data, detached_metadata, error)) - { - g_prefix_error (error, "Can't verify commit"); - return FALSE; - } + return glnx_prefix_error (error, "Can't verify commit"); /* Mark the commit as verified to avoid double verification * see process_verify_result () for rationale */ @@ -2022,6 +2011,8 @@ scan_commit_object (OtPullData *pull_data, !g_hash_table_contains (pull_data->verified_commits, checksum)) { gboolean ret = FALSE; + g_autoptr (GError) verification_error = NULL; + /* list all signature types in detached metadata and check if signed by any? */ g_auto (GStrv) names = ostree_sign_list_names(); for (char **iter=names; !ret && iter && *iter; iter++) @@ -2042,29 +2033,16 @@ scan_commit_object (OtPullData *pull_data, checksum, cancellable, &local_error)) - { - ret = TRUE; - g_clear_error (error); - } + ret = TRUE; } /* Save error message for better reason detection later if needed */ if (!ret) - { - if (*error == NULL) - g_propagate_error (error, g_steal_pointer (&local_error)); - else - g_prefix_error (error, "%s; ", local_error->message); - } + glnx_prefix_error (&verification_error, "%s", local_error->message); } if (!ret) - { - g_prefix_error (error, "Can't verify commit %s: ", checksum); - return FALSE; - } - /* Clear non-fatal error */ - g_clear_error (error); + return glnx_throw (error, "Can't verify commit %s: %s", checksum, verification_error->message); } /* If we found a legacy transaction flag, assume we have to scan. @@ -4444,8 +4422,6 @@ ostree_repo_pull_with_options (OstreeRepo *self, if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures, &temp_error)) { - gboolean ret = FALSE; - if (summary_from_cache) { /* The cached summary doesn't match, fetch a new one and verify again */ @@ -4473,16 +4449,12 @@ ostree_repo_pull_with_options (OstreeRepo *self, cancellable, error)) goto out; - if (_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures, error)) - ret = TRUE; + if (!_ostree_repo_sign_verify (pull_data->repo, pull_data->remote_name, bytes_summary, signatures, error)) + goto out; } else - g_propagate_error (error, g_steal_pointer (&temp_error)); - - - if (!ret) { - g_prefix_error (error, "Can't verify summary: "); + g_propagate_error (error, g_steal_pointer (&temp_error)); goto out; } } diff --git a/tests/test-signed-pull-summary.sh b/tests/test-signed-pull-summary.sh index 7b18cf65..ee731e86 100755 --- a/tests/test-signed-pull-summary.sh +++ b/tests/test-signed-pull-summary.sh @@ -155,7 +155,7 @@ do if ${OSTREE} --repo=repo pull origin main 2>err.txt; then assert_not_reached "Successful pull with invalid ${engine} signature" fi - assert_file_has_content err.txt "Can't verify summary" + assert_file_has_content err.txt "signed with unknown key" mv ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.good,} echo "ok ${engine} pull with invalid ${engine} summary signature fails" @@ -223,7 +223,7 @@ cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.2,} if ${OSTREE} --repo=repo pull origin main 2>err.txt; then assert_not_reached "Successful pull with old summary" fi -assert_file_has_content err.txt "Can't verify summary" +assert_file_has_content err.txt "signed with unknown key" assert_has_file repo/tmp/cache/summaries/origin assert_has_file repo/tmp/cache/summaries/origin.sig cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.1 >&2 From e2c601687b81330f863b59bc42503532c1527ec5 Mon Sep 17 00:00:00 2001 From: Denis Pynkin Date: Mon, 23 Mar 2020 15:54:06 +0300 Subject: [PATCH 75/75] ostree-repo: improve error handling Correctly return "error" from `ostree_repo_sign_commit()` in case if GPG is not enabled. Use glnx_* functions in signature related pull code for clear error handling if GPG isn't enabled. Signed-off-by: Denis Pynkin --- src/libostree/ostree-repo.c | 38 +++++++++---------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 36fde3dc..b4a42b6b 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -2326,10 +2326,7 @@ out: return ret; #else /* OSTREE_DISABLE_GPGME */ - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); - return FALSE; + return glnx_throw (error, "GPG feature is disabled in a build time"); #endif /* OSTREE_DISABLE_GPGME */ } @@ -4974,10 +4971,7 @@ ostree_repo_append_gpg_signature (OstreeRepo *self, return TRUE; #else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); - return FALSE; + return glnx_throw (error, "GPG feature is disabled in a build time"); #endif /* OSTREE_DISABLE_GPGME */ } @@ -5129,7 +5123,7 @@ ostree_repo_sign_commit (OstreeRepo *self, return TRUE; #else /* FIXME: Return false until refactoring */ - return FALSE; + return glnx_throw (error, "GPG feature is disabled in a build time"); #endif /* OSTREE_DISABLE_GPGME */ } @@ -5221,10 +5215,7 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self, return TRUE; #else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); - return FALSE; + return glnx_throw (error, "GPG feature is disabled in a build time"); #endif /* OSTREE_DISABLE_GPGME */ } @@ -5498,10 +5489,7 @@ ostree_repo_verify_commit (OstreeRepo *self, return TRUE; #else /* FIXME: Return false until refactoring */ - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); - return FALSE; + return glnx_throw (error, "GPG feature is disabled in a build time"); #endif /* OSTREE_DISABLE_GPGME */ } @@ -5536,9 +5524,7 @@ ostree_repo_verify_commit_ext (OstreeRepo *self, cancellable, error); #else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); + glnx_throw (error, "GPG feature is disabled in a build time"); return NULL; #endif /* OSTREE_DISABLE_GPGME */ } @@ -5575,9 +5561,7 @@ ostree_repo_verify_commit_for_remote (OstreeRepo *self, cancellable, error); #else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); + glnx_throw (error, "GPG feature is disabled in a build time"); return NULL; #endif /* OSTREE_DISABLE_GPGME */ } @@ -5627,9 +5611,7 @@ ostree_repo_gpg_verify_data (OstreeRepo *self, cancellable, error); #else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); + glnx_throw (error, "GPG feature is disabled in a build time"); return NULL; #endif /* OSTREE_DISABLE_GPGME */ } @@ -5675,9 +5657,7 @@ ostree_repo_verify_summary (OstreeRepo *self, cancellable, error); #else - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "'%s': GPG feature is disabled in a build time", - __FUNCTION__); + glnx_throw (error, "GPG feature is disabled in a build time"); return NULL; #endif /* OSTREE_DISABLE_GPGME */ }