diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am index 060351571..43e092815 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 a7e7e1239..c0a7ac9f2 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 470d23d36..f37d974af 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 3525d9f28..cfc4a3406 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 3d5fd3bcd..4066f3839 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 000000000..e489a988e --- /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 000000000..8bbd407da --- /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 000000000..e3ab6b571 --- /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 000000000..d4a6b56d9 --- /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 000000000..96455f86e --- /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 000000000..f06206aac --- /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 +