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);