Factor out a libotcore
authorColin Walters <walters@verbum.org>
Fri, 7 Jul 2023 20:31:58 +0000 (16:31 -0400)
committerColin Walters <walters@verbum.org>
Tue, 11 Jul 2023 18:08:32 +0000 (14:08 -0400)
This will contain logic shared between ostree-prepare-root
and libostree-1.so.  It will just link to libgio.so, so as
to avoid pulling in e.g. libcurl and other things.

In other words, `ostree-prepare-root` will not link to `libostree-1.so`,
but will pull in just what it needs from this library.

Makefile-libostree.am
Makefile-otcore.am [new file with mode: 0644]
Makefile.am
src/libostree/ostree-sign-ed25519.c
src/libotcore/README.md [new file with mode: 0644]
src/libotcore/otcore-ed25519-verify.c [new file with mode: 0644]
src/libotcore/otcore.h [new file with mode: 0644]

index e616b0ae233d6072528d7345d49d2dbb69325a05..1cb7de54774df4f481ef97b6f1a3542a513177da 100644 (file)
@@ -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 (file)
index 0000000..8accf85
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+
+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)
index ca7dec9e4f3f6bd8dbdd6c40c35f008759130b24..19abc0c1e177a08e97f46e919aee8b3ea2db0956 100644 (file)
@@ -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
index 6ff132600baa2774dee5e08bc1ad76b45b4ec98f..21f380aa482817ff3f4ec87ea5cf1b0a6fcded9b 100644 (file)
 #include "config.h"
 
 #include "ostree-sign-ed25519.h"
+#include "otcore.h"
 #include <libglnx.h>
 #include <ot-checksum-utils.h>
 
-#ifdef HAVE_LIBSODIUM
-#include <sodium.h>
-#define USE_LIBSODIUM
-#else
-
-#if defined(HAVE_OPENSSL)
-#include <openssl/evp.h>
-#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 (file)
index 0000000..967a5d9
--- /dev/null
@@ -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 (file)
index 0000000..1c0ec2b
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+ */
+
+#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
+}
diff --git a/src/libotcore/otcore.h b/src/libotcore/otcore.h
new file mode 100644 (file)
index 0000000..fdbca49
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include "otutil.h"
+#include <stdbool.h>
+
+#ifdef HAVE_LIBSODIUM
+#include <sodium.h>
+#define USE_LIBSODIUM
+#elif defined(HAVE_OPENSSL)
+#include <openssl/evp.h>
+#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);