diff --git a/Makefile-libostree.am b/Makefile-libostree.am index e616b0ae23..1cb7de5477 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -185,12 +185,12 @@ EXTRA_DIST += \ $(top_srcdir)/src/libostree/libostree-released.sym \ $(NULL) -libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/bsdiff -I$(srcdir)/libglnx -I$(srcdir)/composefs -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(builddir)/src/libostree \ +libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/bsdiff -I$(srcdir)/libglnx -I$(srcdir)/composefs -I$(srcdir)/src/libotutil -I$(srcdir)/src/libotcore -I$(srcdir)/src/libostree -I$(builddir)/src/libostree \ $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_LZMA_CFLAGS) $(OT_DEP_ZLIB_CFLAGS) $(OT_DEP_CRYPTO_CFLAGS) \ -fvisibility=hidden '-D_OSTREE_PUBLIC=__attribute__((visibility("default"))) extern' \ -DPKGLIBEXECDIR=\"$(pkglibexecdir)\" libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions $(addprefix $(wl_versionscript_arg),$(symbol_files)) -libostree_1_la_LIBADD = libotutil.la libglnx.la libbsdiff.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) \ +libostree_1_la_LIBADD = libotutil.la libotcore.la libglnx.la libbsdiff.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) \ $(OT_DEP_LZMA_LIBS) $(OT_DEP_ZLIB_LIBS) $(OT_DEP_CRYPTO_LIBS) # Some change between rust-1.21.0-1.fc27 and rust-1.22.1-1.fc27.x86_64 libostree_1_la_LIBADD += $(bupsplitpath) diff --git a/Makefile-otcore.am b/Makefile-otcore.am new file mode 100644 index 0000000000..8accf85844 --- /dev/null +++ b/Makefile-otcore.am @@ -0,0 +1,24 @@ +# 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, see . + +noinst_LTLIBRARIES += libotcore.la + +libotcore_la_SOURCES = \ + src/libotcore/otcore.h \ + src/libotcore/otcore-ed25519-verify.c \ + $(NULL) + +libotcore_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_CRYPTO_LIBS) $(LIBSYSTEMD_CFLAGS) +libotcore_la_LIBADD = $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) $(LIBSYSTEMD_LIBS) $(OT_DEP_CRYPTO_LIBS) diff --git a/Makefile.am b/Makefile.am index ca7dec9e4f..19abc0c1e1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -127,6 +127,7 @@ noinst_LTLIBRARIES += libcomposefs.la endif include Makefile-otutil.am +include Makefile-otcore.am include Makefile-libostree.am include Makefile-ostree.am include Makefile-switchroot.am diff --git a/src/libostree/ostree-sign-ed25519.c b/src/libostree/ostree-sign-ed25519.c index 6ff132600b..21f380aa48 100644 --- a/src/libostree/ostree-sign-ed25519.c +++ b/src/libostree/ostree-sign-ed25519.c @@ -24,31 +24,15 @@ #include "config.h" #include "ostree-sign-ed25519.h" +#include "otcore.h" #include #include -#ifdef HAVE_LIBSODIUM -#include -#define USE_LIBSODIUM -#else - -#if defined(HAVE_OPENSSL) -#include -#define USE_OPENSSL -#endif - -#endif - #undef G_LOG_DOMAIN #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" - -#define OSTREE_SIGN_ED25519_SIG_SIZE 64U -#define OSTREE_SIGN_ED25519_PUBKEY_SIZE 32U #define OSTREE_SIGN_ED25519_SEED_SIZE 32U #define OSTREE_SIGN_ED25519_SECKEY_SIZE \ (OSTREE_SIGN_ED25519_SEED_SIZE + OSTREE_SIGN_ED25519_PUBKEY_SIZE) @@ -108,12 +92,11 @@ _ostree_sign_ed25519_init (OstreeSignEd25519 *self) self->public_keys = NULL; self->revoked_keys = NULL; -#if defined(USE_LIBSODIUM) - if (sodium_init () < 0) - self->state = ED25519_FAILED_INITIALIZATION; -#elif defined(USE_OPENSSL) -#else +#if !(defined(USE_OPENSSL) || defined(USE_LIBSODIUM)) self->state = ED25519_NOT_SUPPORTED; +#else + if (!otcore_ed25519_init ()) + self->state = ED25519_FAILED_INITIALIZATION; #endif } @@ -232,7 +215,6 @@ ostree_sign_ed25519_data_verify (OstreeSign *self, GBytes *data, GVariant *signa { g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i); g_autoptr (GBytes) signature = g_variant_get_data_as_bytes (child); - gboolean valid = FALSE; if (g_bytes_get_size (signature) != OSTREE_SIGN_ED25519_SIG_SIZE) return glnx_throw ( @@ -246,7 +228,6 @@ ostree_sign_ed25519_data_verify (OstreeSign *self, GBytes *data, GVariant *signa for (GList *public_key = sign->public_keys; 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) @@ -256,32 +237,12 @@ ostree_sign_ed25519_data_verify (OstreeSign *self, GBytes *data, GVariant *signa continue; } -#if defined(USE_LIBSODIUM) - valid = 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; -#elif defined(USE_OPENSSL) - EVP_MD_CTX *ctx = EVP_MD_CTX_new (); - if (!ctx) - return glnx_throw (error, "openssl: failed to allocate context"); - EVP_PKEY *pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, public_key->data, - OSTREE_SIGN_ED25519_PUBKEY_SIZE); - if (!pkey) - { - EVP_MD_CTX_free (ctx); - return glnx_throw (error, "openssl: Failed to initialize ed5519 key"); - } - - valid = EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, pkey) != 0 - && EVP_DigestVerify (ctx, g_bytes_get_data (signature, NULL), - g_bytes_get_size (signature), g_bytes_get_data (data, NULL), - g_bytes_get_size (data)) - != 0; - - EVP_PKEY_free (pkey); - EVP_MD_CTX_free (ctx); -#endif + bool valid = false; + // Wrap the pubkey in a GBytes as that's what this API wants + g_autoptr (GBytes) public_key_bytes + = g_bytes_new_static (public_key->data, OSTREE_SIGN_ED25519_PUBKEY_SIZE); + if (!otcore_validate_ed25519_signature (data, public_key_bytes, signature, &valid, error)) + return FALSE; if (!valid) { /* Incorrect signature! */ diff --git a/src/libotcore/README.md b/src/libotcore/README.md new file mode 100644 index 0000000000..967a5d9f63 --- /dev/null +++ b/src/libotcore/README.md @@ -0,0 +1 @@ +This library is (will be) shared between `libostree-1.so` and `ostree-prepare-root`. diff --git a/src/libotcore/otcore-ed25519-verify.c b/src/libotcore/otcore-ed25519-verify.c new file mode 100644 index 0000000000..34383a43ec --- /dev/null +++ b/src/libotcore/otcore-ed25519-verify.c @@ -0,0 +1,115 @@ +/* + * 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, see . + */ + +#include "config.h" + +#include "otcore.h" + +/* Initialize global state; may be called multiple times and is idempotent. */ +bool +otcore_ed25519_init (void) +{ +#if defined(HAVE_LIBSODIUM) + static gssize initstate; + if (g_once_init_enter (&initstate)) + { + int val = sodium_init () >= 0 ? 1 : -1; + g_once_init_leave (&initstate, val); + } + switch (initstate) + { + case 1: + return true; + case -1: + return false; + default: + g_assert_not_reached (); + } +#else + return true; +#endif +} + +/* Validate a single ed25519 signature. If there is an unexpected state, such + * as an ill-forumed public key or signature, a hard error will be returned. + * + * If the signature is not correct, this function will return successfully, but + * `out_valid` will be set to `false`. + * + * If the signature is correct, `out_valid` will be `true`. + * */ +gboolean +otcore_validate_ed25519_signature (GBytes *data, GBytes *public_key, GBytes *signature, + bool *out_valid, GError **error) +{ + // Since this is signature verification code, let's verify preconditions. + g_assert (data); + g_assert (public_key); + g_assert (signature); + g_assert (out_valid); + // It is OK for error to be NULL, though according to GError rules. + +#if defined(HAVE_LIBSODIUM) || defined(HAVE_OPENSSL) + // And strictly verify pubkey and signature lengths + if (g_bytes_get_size (public_key) != OSTREE_SIGN_ED25519_PUBKEY_SIZE) + return glnx_throw (error, "Invalid public key of %" G_GSIZE_FORMAT " expected %" G_GSIZE_FORMAT, + (gsize)g_bytes_get_size (public_key), + (gsize)OSTREE_SIGN_ED25519_PUBKEY_SIZE); + const guint8 *public_key_buf = g_bytes_get_data (public_key, NULL); + if (g_bytes_get_size (signature) != OSTREE_SIGN_ED25519_SIG_SIZE) + return glnx_throw ( + error, "Invalid signature length of %" G_GSIZE_FORMAT " bytes, expected %" G_GSIZE_FORMAT, + (gsize)g_bytes_get_size (signature), (gsize)OSTREE_SIGN_ED25519_SIG_SIZE); + const guint8 *signature_buf = g_bytes_get_data (signature, NULL); + +#endif + +#if defined(HAVE_LIBSODIUM) + // Note that libsodium assumes the passed byte arrays for the signature and public key + // have at least the expected length, but we checked that above. + if (crypto_sign_verify_detached (signature_buf, g_bytes_get_data (data, NULL), + g_bytes_get_size (data), public_key_buf) + == 0) + { + *out_valid = true; + } + return TRUE; +#elif defined(HAVE_OPENSSL) + EVP_MD_CTX *ctx = EVP_MD_CTX_new (); + if (!ctx) + return glnx_throw (error, "openssl: failed to allocate context"); + EVP_PKEY *pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, public_key_buf, + OSTREE_SIGN_ED25519_PUBKEY_SIZE); + if (!pkey) + { + EVP_MD_CTX_free (ctx); + return glnx_throw (error, "openssl: Failed to initialize ed5519 key"); + } + if (EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, pkey) != 0 + && EVP_DigestVerify (ctx, signature_buf, OSTREE_SIGN_ED25519_SIG_SIZE, + g_bytes_get_data (data, NULL), g_bytes_get_size (data)) + != 0) + { + *out_valid = true; + } + EVP_PKEY_free (pkey); + EVP_MD_CTX_free (ctx); + return TRUE; +#else + return glnx_throw (error, "ed25519 signature validation requested, but support not compiled in"); +#endif +} \ No newline at end of file diff --git a/src/libotcore/otcore.h b/src/libotcore/otcore.h new file mode 100644 index 0000000000..fdbca4932c --- /dev/null +++ b/src/libotcore/otcore.h @@ -0,0 +1,44 @@ +/* + * 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, see . + */ + +#pragma once + +#include "config.h" + +#include "otutil.h" +#include + +#ifdef HAVE_LIBSODIUM +#include +#define USE_LIBSODIUM +#elif defined(HAVE_OPENSSL) +#include +#define USE_OPENSSL +#endif + +// Length of a signature in bytes +#define OSTREE_SIGN_ED25519_SIG_SIZE 64U +// Length of a public key in bytes +#define OSTREE_SIGN_ED25519_PUBKEY_SIZE 32U +// This key is stored inside commit metadata. +#define OSTREE_SIGN_METADATA_ED25519_KEY "ostree.sign.ed25519" +// The variant type +#define OSTREE_SIGN_METADATA_ED25519_TYPE "aay" + +bool otcore_ed25519_init (void); +gboolean otcore_validate_ed25519_signature (GBytes *data, GBytes *pubkey, GBytes *signature, + bool *out_valid, GError **error);