mirror of
https://github.com/ostreedev/ostree.git
synced 2024-12-21 13:34:34 +03:00
Compare commits
12 Commits
49b6a6ab7a
...
47d529e8c3
Author | SHA1 | Date | |
---|---|---|---|
|
47d529e8c3 | ||
|
8aaea0c65d | ||
|
45ddf3b798 | ||
|
aca6f17ff8 | ||
|
66f5a77ae6 | ||
|
786b38c2cf | ||
|
e83eda0aec | ||
|
5583563b8f | ||
|
0fa1061779 | ||
|
ef44e33def | ||
|
62e54529e4 | ||
|
334e0b0d2e |
@ -48,6 +48,7 @@ libostree_public_headers = \
|
||||
src/libostree/ostree-kernel-args.h \
|
||||
src/libostree/ostree-sign.h \
|
||||
src/libostree/ostree-sign-ed25519.h \
|
||||
src/libostree/ostree-blob-reader.h \
|
||||
$(NULL)
|
||||
|
||||
# This one is generated via configure.ac, and the gtk-doc
|
||||
|
@ -175,9 +175,9 @@ endif # USE_GPGME
|
||||
symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym
|
||||
|
||||
# Uncomment this include when adding new development symbols.
|
||||
#if BUILDOPT_IS_DEVEL_BUILD
|
||||
#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
|
||||
#endif
|
||||
if BUILDOPT_IS_DEVEL_BUILD
|
||||
symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
|
||||
endif
|
||||
|
||||
# http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
|
||||
wl_versionscript_arg = -Wl,--version-script=
|
||||
@ -261,7 +261,18 @@ libostree_1_la_SOURCES += \
|
||||
src/libostree/ostree-sign-dummy.h \
|
||||
src/libostree/ostree-sign-ed25519.c \
|
||||
src/libostree/ostree-sign-ed25519.h \
|
||||
src/libostree/ostree-sign-spki.c \
|
||||
src/libostree/ostree-sign-spki.h \
|
||||
src/libostree/ostree-sign-private.h \
|
||||
src/libostree/ostree-blob-reader.c \
|
||||
src/libostree/ostree-blob-reader.h \
|
||||
src/libostree/ostree-blob-reader-base64.c \
|
||||
src/libostree/ostree-blob-reader-base64.h \
|
||||
src/libostree/ostree-blob-reader-raw.c \
|
||||
src/libostree/ostree-blob-reader-raw.h \
|
||||
src/libostree/ostree-blob-reader-pem.c \
|
||||
src/libostree/ostree-blob-reader-pem.h \
|
||||
src/libostree/ostree-blob-reader-private.h \
|
||||
$(NULL)
|
||||
|
||||
if USE_COMPOSEFS
|
||||
|
@ -19,6 +19,7 @@ libotcore_la_SOURCES = \
|
||||
src/libotcore/otcore.h \
|
||||
src/libotcore/otcore-ed25519-verify.c \
|
||||
src/libotcore/otcore-prepare-root.c \
|
||||
src/libotcore/otcore-spki-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)
|
||||
|
@ -156,12 +156,24 @@ _installed_or_uninstalled_test_scripts = \
|
||||
tests/test-summary-collections.sh \
|
||||
tests/test-pull-collections.sh \
|
||||
tests/test-config.sh \
|
||||
tests/test-signed-commit.sh \
|
||||
tests/test-signed-commit-dummy.sh \
|
||||
tests/test-signed-pull.sh \
|
||||
tests/test-pre-signed-pull.sh \
|
||||
tests/test-signed-pull-summary.sh \
|
||||
$(NULL)
|
||||
|
||||
if HAVE_ED25519
|
||||
_installed_or_uninstalled_test_scripts += \
|
||||
tests/test-signed-commit-ed25519.sh \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
if HAVE_SPKI
|
||||
_installed_or_uninstalled_test_scripts += \
|
||||
tests/test-signed-commit-spki.sh \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
if USE_GPGME
|
||||
_installed_or_uninstalled_test_scripts += \
|
||||
tests/test-remote-gpg-import.sh \
|
||||
@ -270,7 +282,7 @@ _installed_or_uninstalled_test_programs = tests/test-varint tests/test-ot-unix-u
|
||||
tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \
|
||||
tests/test-checksum tests/test-lzma tests/test-rollsum \
|
||||
tests/test-basic-c tests/test-sysroot-c tests/test-pull-c tests/test-repo tests/test-include-ostree-h tests/test-kargs \
|
||||
tests/test-rfc2616-dates
|
||||
tests/test-rfc2616-dates tests/test-pem
|
||||
|
||||
if USE_GPGME
|
||||
_installed_or_uninstalled_test_programs += \
|
||||
@ -403,6 +415,12 @@ tests_test_rfc2616_dates_SOURCES = \
|
||||
tests_test_rfc2616_dates_CFLAGS = $(TESTS_CFLAGS)
|
||||
tests_test_rfc2616_dates_LDADD = $(TESTS_LDADD)
|
||||
|
||||
tests_test_pem_SOURCES = \
|
||||
src/libostree/ostree-blob-reader-pem.c \
|
||||
tests/test-pem.c
|
||||
tests_test_pem_CFLAGS = $(TESTS_CFLAGS)
|
||||
tests_test_pem_LDADD = $(TESTS_LDADD)
|
||||
|
||||
noinst_PROGRAMS += tests/test-commit-sign-sh-ext
|
||||
tests_test_commit_sign_sh_ext_CFLAGS = $(TESTS_CFLAGS)
|
||||
tests_test_commit_sign_sh_ext_LDADD = $(TESTS_LDADD)
|
||||
|
@ -767,6 +767,15 @@ ostree_sign_metadata_key
|
||||
ostree_sign_set_pk
|
||||
ostree_sign_set_sk
|
||||
ostree_sign_summary
|
||||
ostree_sign_read_pk
|
||||
ostree_sign_read_sk
|
||||
<SUBSECTION Standard>
|
||||
ostree_sign_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>ostree-blob-reader</FILE>
|
||||
ostree_blob_reader_read_blob
|
||||
<SUBSECTION Standard>
|
||||
ostree_blob_reader_get_type
|
||||
</SECTION>
|
||||
|
17
configure.ac
17
configure.ac
@ -1,7 +1,7 @@
|
||||
AC_PREREQ([2.63])
|
||||
dnl To perform a release, follow the instructions in `docs/CONTRIBUTING.md`.
|
||||
m4_define([year_version], [2024])
|
||||
m4_define([release_version], [10])
|
||||
m4_define([release_version], [11])
|
||||
m4_define([package_version], [year_version.release_version])
|
||||
AC_INIT([libostree], [package_version], [walters@verbum.org])
|
||||
is_release_build=no
|
||||
@ -452,10 +452,19 @@ if test x$with_openssl != xno; then OSTREE_FEATURES="$OSTREE_FEATURES openssl";
|
||||
AM_CONDITIONAL(USE_OPENSSL, test $with_openssl != no)
|
||||
dnl end openssl
|
||||
|
||||
if test x$with_openssl != xno || test x$with_ed25519_libsodium != xno; then
|
||||
AM_CONDITIONAL([HAVE_ED25519], [test x$with_openssl != xno || test x$with_ed25519_libsodium != xno])
|
||||
|
||||
AM_COND_IF([HAVE_ED25519], [
|
||||
AC_DEFINE([HAVE_ED25519], 1, [Define if ed25519 is supported ])
|
||||
OSTREE_FEATURES="$OSTREE_FEATURES sign-ed25519"
|
||||
fi
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([HAVE_SPKI], [test x$with_openssl != xno])
|
||||
|
||||
AM_COND_IF([HAVE_SPKI], [
|
||||
AC_DEFINE([HAVE_SPKI], 1, [Define if spki is supported ])
|
||||
OSTREE_FEATURES="$OSTREE_FEATURES sign-spki"
|
||||
])
|
||||
|
||||
dnl begin gnutls; in contrast to openssl this one only
|
||||
dnl supports --with-crypto=gnutls
|
||||
@ -697,7 +706,7 @@ echo "
|
||||
systemd: $with_libsystemd
|
||||
libmount: $with_libmount
|
||||
libsodium (ed25519 signatures): $with_ed25519_libsodium
|
||||
openssl (ed25519 signatures): $with_openssl
|
||||
openssl (ed25519 and spki signatures): $with_openssl
|
||||
libarchive (parse tar files directly): $with_libarchive
|
||||
static deltas: yes (always enabled now)
|
||||
O_TMPFILE: $enable_otmpfile
|
||||
|
@ -312,7 +312,7 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
<term><option>-s, --sign-type</option></term>
|
||||
<listitem><para>
|
||||
Use particular signature engine. Currently
|
||||
available <arg choice="plain">ed25519</arg> and <arg choice="plain">dummy</arg>
|
||||
available <arg choice="plain">ed25519</arg>, <arg choice="plain">spki</arg>, and <arg choice="plain">dummy</arg>
|
||||
signature types.
|
||||
|
||||
The default is <arg choice="plain">ed25519</arg>.
|
||||
@ -323,7 +323,8 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
<varlistentry>
|
||||
<term><option>--sign-from-file</option>="PATH"</term>
|
||||
<listitem><para>
|
||||
This will read a key (corresponding to the provided <literal>--sign-type</literal> from the provided path. The key should be base64 encoded.
|
||||
This will read a key (corresponding to the provided <literal>--sign-type</literal> from the provided path. The encoding of the key depends on
|
||||
signature engine. For ed25519 the key should be base64 encoded, for spki it should be in PEM format, and for dummy it should be an ASCII-string.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -337,7 +338,7 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
The <literal>KEY-ID</literal> is:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>for ed25519:</option></term>
|
||||
<term><option>for ed25519 and spki:</option></term>
|
||||
<listitem><para>
|
||||
<literal>base64</literal>-encoded secret key for commit signing.
|
||||
</para></listitem>
|
||||
|
@ -120,20 +120,25 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
<varlistentry>
|
||||
<term><varname>root.transient</varname></term>
|
||||
<listitem><para>A boolean value; the default is <literal>false</literal>.
|
||||
If this is set to <literal>true</literal>, then the <literal>/</literal> filesystem will be a writable <literal>overlayfs</literal>,
|
||||
with the upper directory being a hidden directory (in the underlying system root filesystem) that will persist across reboots by default.
|
||||
However, changes will <emphasis>be discarded</emphasis> on OS updates!
|
||||
Setting this flag to <literal>true</literal> requires composefs (See <literal>composefs.enabled</literal>).
|
||||
When enabled, the root mount point <literal>/</literal> will be an overlayfs whose contents will be stored
|
||||
in a tmpfs, and hence discarded on OS upgrade or reboot.
|
||||
</para>
|
||||
<para>
|
||||
Enabling this option can be very useful for cases such as packages (dpkg/rpm/etc) that write content into <literal>/opt</literal>,
|
||||
particularly where they expect the target to be writable at runtime. To make that work, ensure that your <literal>/opt</literal>
|
||||
directory is *not* a symlink to <literal>/var/opt</literal>, but is just an empty directory.
|
||||
</para>
|
||||
<para>
|
||||
Note the <literal>/usr</literal> mount point remains read-only by default. This option is independent of <literal>etc.transient</literal> and <literal>sysroot.readonly</literal>;
|
||||
This option is independent of <literal>etc.transient</literal> and <literal>sysroot.readonly</literal>;
|
||||
it is supported for example to have <literal>root.transient=true</literal> but <literal>etc.transient=false</literal> in which case changes to <literal>/etc</literal> continue
|
||||
to persist across updates, with the default OSTree 3-way merge applied.
|
||||
</para></listitem>
|
||||
Also related to persistence it is important to emphasize that <literal>/sysroot</literal> (the physical root filesystem) is still persistent
|
||||
by default; in-place OS upgrades can be applied.
|
||||
</para>
|
||||
<para>
|
||||
Enabling this option can make it significantly easier to adopt an image-based model in some circumstances.
|
||||
For example, if you have a configuration management system that is inspecting machine-specific state and
|
||||
e.g. dynamically installing packages or applying configuration, it can more easily be adapted to
|
||||
run on each boot, while still shifting a portion (or ideally most) image configuration to build time
|
||||
as part of the base image/commit.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>composefs.enabled</varname></term>
|
||||
|
@ -64,26 +64,28 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are several "well-known" system places for `ed25519` trusted and revoked public keys -- expected single <literal>base64</literal>-encoded key per line.
|
||||
For `ed25519` and `spki`, there are several "well-known" system places for trusted and revoked public keys as listed below.
|
||||
</para>
|
||||
|
||||
<para>Files:
|
||||
<itemizedlist>
|
||||
<listitem><para><filename>/etc/ostree/trusted.ed25519</filename></para></listitem>
|
||||
<listitem><para><filename>/etc/ostree/revoked.ed25519</filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/trusted.ed25519</filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/revoked.ed25519</filename></para></listitem>
|
||||
<listitem><para><filename>/etc/ostree/trusted.<replaceable>SIGN-TYPE</replaceable></filename></para></listitem>
|
||||
<listitem><para><filename>/etc/ostree/revoked.<replaceable>SIGN-TYPE</replaceable></filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/trusted.<replaceable>SIGN-TYPE</replaceable></filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/revoked.<replaceable>SIGN-TYPE</replaceable></filename></para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>Directories containing files with keys:
|
||||
<itemizedlist>
|
||||
<listitem><para><filename>/etc/ostree/trusted.ed25519.d</filename></para></listitem>
|
||||
<listitem><para><filename>/etc/ostree/revoked.ed25519.d</filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/trusted.ed25519.d</filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/rvokeded.ed25519.d</filename></para></listitem>
|
||||
<listitem><para><filename>/etc/ostree/trusted.<replaceable>SIGN-TYPE</replaceable>.d</filename></para></listitem>
|
||||
<listitem><para><filename>/etc/ostree/revoked.<replaceable>SIGN-TYPE</replaceable>.d</filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/trusted.<replaceable>SIGN-TYPE</replaceable>.d</filename></para></listitem>
|
||||
<listitem><para><filename>/usr/share/ostree/revoked.<replaceable>SIGN-TYPE</replaceable>.d</filename></para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>The format of those files depends on the signature mechanism; for `ed25519`, keys are stored in the <literal>base64</literal> encoding per line, while for `spki` they are stored in the PEM "PUBLIC KEY" encoding.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@ -95,7 +97,7 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
<listitem><para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>for ed25519:</option></term>
|
||||
<term><option>for ed25519 and spki:</option></term>
|
||||
<listitem><para>
|
||||
<literal>base64</literal>-encoded secret (for signing) or public key (for verifying).
|
||||
</para></listitem>
|
||||
@ -120,7 +122,7 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
<term><option>-s, --sign-type</option></term>
|
||||
<listitem><para>
|
||||
Use particular signature mechanism. Currently
|
||||
available <arg choice="plain">ed25519</arg> and <arg choice="plain">dummy</arg>
|
||||
available <arg choice="plain">ed25519</arg>, <arg choice="plain">spki</arg>, and <arg choice="plain">dummy</arg>
|
||||
signature types.
|
||||
|
||||
The default is <arg choice="plain">ed25519</arg>.
|
||||
@ -133,8 +135,8 @@ License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
Valid for <literal>ed25519</literal> signature type.
|
||||
For <literal>ed25519</literal> this file must contain <literal>base64</literal>-encoded
|
||||
Valid for <literal>ed25519</literal> and <literal>spki</literal> signature types.
|
||||
This file must contain <literal>base64</literal>-encoded
|
||||
secret key(s) (for signing) or public key(s) (for verifying) per line.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
@ -157,6 +157,7 @@ main ()
|
||||
PRINT_CONSTANT (OSTREE_SHA256_DIGEST_LEN);
|
||||
PRINT_CONSTANT (OSTREE_SHA256_STRING_LEN);
|
||||
PRINT_CONSTANT (OSTREE_SIGN_NAME_ED25519);
|
||||
PRINT_CONSTANT (OSTREE_SIGN_NAME_SPKI);
|
||||
PRINT_CONSTANT ((gint)OSTREE_STATIC_DELTA_GENERATE_OPT_LOWLATENCY);
|
||||
PRINT_CONSTANT ((gint)OSTREE_STATIC_DELTA_GENERATE_OPT_MAJOR);
|
||||
PRINT_CONSTANT ((gint)OSTREE_STATIC_DELTA_INDEX_FLAGS_NONE);
|
||||
|
@ -20,6 +20,14 @@
|
||||
- uncomment the include in Makefile-libostree.am
|
||||
*/
|
||||
|
||||
LIBOSTREE_2024.8 {
|
||||
global:
|
||||
ostree_sign_read_pk;
|
||||
ostree_sign_read_sk;
|
||||
ostree_blob_reader_get_type;
|
||||
ostree_blob_reader_read_blob;
|
||||
} LIBOSTREE_2024.7;
|
||||
|
||||
/* Stub section for the stable release *after* this development one; don't
|
||||
* edit this other than to update the year. This is just a copy/paste
|
||||
* source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION
|
||||
|
80
src/libostree/ostree-blob-reader-base64.c
Normal file
80
src/libostree/ostree-blob-reader-base64.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-blob-reader-base64.h"
|
||||
|
||||
struct _OstreeBlobReaderBase64
|
||||
{
|
||||
GDataInputStream parent_instance;
|
||||
};
|
||||
|
||||
static void ostree_blob_reader_base64_iface_init (OstreeBlobReaderInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (OstreeBlobReaderBase64, _ostree_blob_reader_base64,
|
||||
G_TYPE_DATA_INPUT_STREAM,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BLOB_READER,
|
||||
ostree_blob_reader_base64_iface_init));
|
||||
|
||||
static void
|
||||
ostree_blob_reader_base64_iface_init (OstreeBlobReaderInterface *iface)
|
||||
{
|
||||
iface->read_blob = ostree_blob_reader_base64_read_blob;
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_blob_reader_base64_class_init (OstreeBlobReaderBase64Class *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_blob_reader_base64_init (OstreeBlobReaderBase64 *self)
|
||||
{
|
||||
}
|
||||
|
||||
OstreeBlobReaderBase64 *
|
||||
_ostree_blob_reader_base64_new (GInputStream *stream)
|
||||
{
|
||||
return g_object_new (OSTREE_TYPE_BLOB_READER_BASE64, "base-stream", stream, NULL);
|
||||
}
|
||||
|
||||
GBytes *
|
||||
ostree_blob_reader_base64_read_blob (OstreeBlobReader *self, GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gsize len = 0;
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autofree char *line
|
||||
= g_data_input_stream_read_line (G_DATA_INPUT_STREAM (self), &len, cancellable, &local_error);
|
||||
if (local_error != NULL)
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (line == NULL)
|
||||
return NULL;
|
||||
|
||||
gsize n_elements;
|
||||
g_base64_decode_inplace (line, &n_elements);
|
||||
explicit_bzero (line + n_elements, len - n_elements);
|
||||
|
||||
return g_bytes_new_take (g_steal_pointer (&line), n_elements);
|
||||
}
|
39
src/libostree/ostree-blob-reader-base64.h
Normal file
39
src/libostree/ostree-blob-reader-base64.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-blob-reader.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_BLOB_READER_BASE64 (_ostree_blob_reader_base64_get_type ())
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_FINAL_TYPE (OstreeBlobReaderBase64, _ostree_blob_reader_base64, OSTREE,
|
||||
BLOB_READER_BASE64, GDataInputStream);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeBlobReaderBase64 *_ostree_blob_reader_base64_new (GInputStream *stream);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GBytes *ostree_blob_reader_base64_read_blob (OstreeBlobReader *self, GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
225
src/libostree/ostree-blob-reader-pem.c
Normal file
225
src/libostree/ostree-blob-reader-pem.c
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/* This implements a simple parser of the PEM format defined in RFC
|
||||
* 7468, which doesn't allow headers to be encoded alongside the data
|
||||
* (unlike the legacy RFC 1421).
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ostree-blob-reader-pem.h"
|
||||
#include "ostree-blob-reader-private.h"
|
||||
#include <string.h>
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_LABEL
|
||||
};
|
||||
|
||||
struct _OstreeBlobReaderPem
|
||||
{
|
||||
GDataInputStream parent_instance;
|
||||
|
||||
gchar *label;
|
||||
};
|
||||
|
||||
static void ostree_blob_reader_pem_iface_init (OstreeBlobReaderInterface *iface);
|
||||
G_DEFINE_TYPE_WITH_CODE (OstreeBlobReaderPem, _ostree_blob_reader_pem, G_TYPE_DATA_INPUT_STREAM,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BLOB_READER,
|
||||
ostree_blob_reader_pem_iface_init));
|
||||
|
||||
static void
|
||||
ostree_blob_reader_pem_iface_init (OstreeBlobReaderInterface *iface)
|
||||
{
|
||||
iface->read_blob = ostree_blob_reader_pem_read_blob;
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_blob_reader_pem_set_property (GObject *object, guint prop_id, const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
OstreeBlobReaderPem *self = OSTREE_BLOB_READER_PEM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LABEL:
|
||||
self->label = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_blob_reader_pem_get_property (GObject *object, guint prop_id, GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
OstreeBlobReaderPem *self = OSTREE_BLOB_READER_PEM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LABEL:
|
||||
g_value_set_string (value, self->label);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ostree_blob_reader_pem_finalize (GObject *object)
|
||||
{
|
||||
OstreeBlobReaderPem *self = OSTREE_BLOB_READER_PEM (object);
|
||||
|
||||
g_free (self->label);
|
||||
|
||||
G_OBJECT_CLASS (_ostree_blob_reader_pem_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_blob_reader_pem_class_init (OstreeBlobReaderPemClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = ostree_blob_reader_pem_set_property;
|
||||
gobject_class->get_property = ostree_blob_reader_pem_get_property;
|
||||
gobject_class->finalize = ostree_blob_reader_pem_finalize;
|
||||
|
||||
/*
|
||||
* OstreeBlobReaderPem:label:
|
||||
*
|
||||
* The label to filter the PEM blocks.
|
||||
*/
|
||||
g_object_class_install_property (
|
||||
gobject_class, PROP_LABEL,
|
||||
g_param_spec_string ("label", "", "", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_blob_reader_pem_init (OstreeBlobReaderPem *self)
|
||||
{
|
||||
}
|
||||
|
||||
OstreeBlobReaderPem *
|
||||
_ostree_blob_reader_pem_new (GInputStream *base, const gchar *label)
|
||||
{
|
||||
g_assert (G_IS_INPUT_STREAM (base));
|
||||
|
||||
return g_object_new (OSTREE_TYPE_BLOB_READER_PEM, "base-stream", base, "label", label, NULL);
|
||||
}
|
||||
|
||||
enum PemInputState
|
||||
{
|
||||
PEM_INPUT_STATE_OUTER,
|
||||
PEM_INPUT_STATE_INNER
|
||||
};
|
||||
|
||||
#define PEM_SUFFIX "-----"
|
||||
#define PEM_PREFIX_BEGIN "-----BEGIN "
|
||||
#define PEM_PREFIX_END "-----END "
|
||||
|
||||
GBytes *
|
||||
_ostree_read_pem_block (GDataInputStream *stream, gchar **label, GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
enum PemInputState state = PEM_INPUT_STATE_OUTER;
|
||||
g_autofree gchar *tmp_label = NULL;
|
||||
g_autoptr (GString) buf = g_string_new ("");
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
gsize length;
|
||||
g_autofree gchar *line = g_data_input_stream_read_line (stream, &length, cancellable, error);
|
||||
if (!line)
|
||||
break;
|
||||
|
||||
line = g_strstrip (line);
|
||||
if (*line == '\0')
|
||||
continue;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PEM_INPUT_STATE_OUTER:
|
||||
if (g_str_has_prefix (line, PEM_PREFIX_BEGIN) && g_str_has_suffix (line, PEM_SUFFIX))
|
||||
{
|
||||
const gchar *start = line + sizeof (PEM_PREFIX_BEGIN) - 1;
|
||||
const gchar *end = g_strrstr (start + 1, PEM_SUFFIX);
|
||||
|
||||
tmp_label = g_strndup (start, end - start);
|
||||
state = PEM_INPUT_STATE_INNER;
|
||||
}
|
||||
break;
|
||||
|
||||
case PEM_INPUT_STATE_INNER:
|
||||
if (g_str_has_prefix (line, PEM_PREFIX_END) && g_str_has_suffix (line, PEM_SUFFIX))
|
||||
{
|
||||
const gchar *start = line + sizeof (PEM_PREFIX_END) - 1;
|
||||
const gchar *end = g_strrstr (start + 1, PEM_SUFFIX);
|
||||
|
||||
if (strncmp (tmp_label, start, end - start) != 0)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Unmatched PEM header");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_base64_decode_inplace (buf->str, &buf->len);
|
||||
GBytes *result = g_bytes_new_take (buf->str, buf->len);
|
||||
|
||||
/* Don't leak the trailing encoded bytes */
|
||||
explicit_bzero (buf->str + buf->len, buf->allocated_len - buf->len);
|
||||
g_string_free (buf, FALSE);
|
||||
buf = NULL;
|
||||
|
||||
if (label)
|
||||
*label = g_steal_pointer (&tmp_label);
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
g_string_append (buf, line);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
if (state != PEM_INPUT_STATE_OUTER)
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "PEM trailer not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GBytes *
|
||||
ostree_blob_reader_pem_read_blob (OstreeBlobReader *self, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
OstreeBlobReaderPem *pself = OSTREE_BLOB_READER_PEM (self);
|
||||
|
||||
g_autofree gchar *label = NULL;
|
||||
g_autoptr (GBytes) blob
|
||||
= _ostree_read_pem_block (G_DATA_INPUT_STREAM (self), &label, cancellable, error);
|
||||
if (blob != NULL && !g_str_equal (label, pself->label))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unexpected label \"%s\"", label);
|
||||
g_clear_pointer (&blob, g_bytes_unref);
|
||||
}
|
||||
return g_steal_pointer (&blob);
|
||||
}
|
39
src/libostree/ostree-blob-reader-pem.h
Normal file
39
src/libostree/ostree-blob-reader-pem.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-blob-reader.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_BLOB_READER_PEM (_ostree_blob_reader_pem_get_type ())
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_FINAL_TYPE (OstreeBlobReaderPem, _ostree_blob_reader_pem, OSTREE, BLOB_READER_PEM,
|
||||
GDataInputStream);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeBlobReaderPem *_ostree_blob_reader_pem_new (GInputStream *base, const gchar *label);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GBytes *ostree_blob_reader_pem_read_blob (OstreeBlobReader *self, GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
33
src/libostree/ostree-blob-reader-private.h
Normal file
33
src/libostree/ostree-blob-reader-private.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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
|
||||
|
||||
#ifndef __GI_SCANNER__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GBytes *_ostree_read_pem_block (GDataInputStream *stream, gchar **label, GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
74
src/libostree/ostree-blob-reader-raw.c
Normal file
74
src/libostree/ostree-blob-reader-raw.c
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-blob-reader-raw.h"
|
||||
|
||||
struct _OstreeBlobReaderRaw
|
||||
{
|
||||
GDataInputStream parent_instance;
|
||||
};
|
||||
|
||||
static void ostree_blob_reader_raw_iface_init (OstreeBlobReaderInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (OstreeBlobReaderRaw, _ostree_blob_reader_raw, G_TYPE_DATA_INPUT_STREAM,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BLOB_READER,
|
||||
ostree_blob_reader_raw_iface_init));
|
||||
|
||||
static void
|
||||
ostree_blob_reader_raw_iface_init (OstreeBlobReaderInterface *iface)
|
||||
{
|
||||
iface->read_blob = ostree_blob_reader_raw_read_blob;
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_blob_reader_raw_class_init (OstreeBlobReaderRawClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_blob_reader_raw_init (OstreeBlobReaderRaw *self)
|
||||
{
|
||||
}
|
||||
|
||||
OstreeBlobReaderRaw *
|
||||
_ostree_blob_reader_raw_new (GInputStream *stream)
|
||||
{
|
||||
return g_object_new (OSTREE_TYPE_BLOB_READER_RAW, "base-stream", stream, NULL);
|
||||
}
|
||||
|
||||
GBytes *
|
||||
ostree_blob_reader_raw_read_blob (OstreeBlobReader *self, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
gsize len = 0;
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autofree char *line
|
||||
= g_data_input_stream_read_line (G_DATA_INPUT_STREAM (self), &len, cancellable, &local_error);
|
||||
if (local_error != NULL)
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (line == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_bytes_new_take (g_steal_pointer (&line), len);
|
||||
}
|
39
src/libostree/ostree-blob-reader-raw.h
Normal file
39
src/libostree/ostree-blob-reader-raw.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-blob-reader.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_BLOB_READER_RAW (_ostree_blob_reader_raw_get_type ())
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_FINAL_TYPE (OstreeBlobReaderRaw, _ostree_blob_reader_raw, OSTREE, BLOB_READER_RAW,
|
||||
GDataInputStream);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeBlobReaderRaw *_ostree_blob_reader_raw_new (GInputStream *stream);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GBytes *ostree_blob_reader_raw_read_blob (OstreeBlobReader *self, GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
37
src/libostree/ostree-blob-reader.c
Normal file
37
src/libostree/ostree-blob-reader.c
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-blob-reader.h"
|
||||
|
||||
G_DEFINE_INTERFACE (OstreeBlobReader, ostree_blob_reader, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
ostree_blob_reader_default_init (OstreeBlobReaderInterface *iface)
|
||||
{
|
||||
g_debug ("OstreeBlobReader initialization");
|
||||
}
|
||||
|
||||
GBytes *
|
||||
ostree_blob_reader_read_blob (OstreeBlobReader *self, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_BLOB_READER (self));
|
||||
return OSTREE_BLOB_READER_GET_IFACE (self)->read_blob (self, cancellable, error);
|
||||
}
|
42
src/libostree/ostree-blob-reader.h
Normal file
42
src/libostree/ostree-blob-reader.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-types.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_BLOB_READER (ostree_blob_reader_get_type ())
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_INTERFACE (OstreeBlobReader, ostree_blob_reader, OSTREE, BLOB_READER, GObject)
|
||||
|
||||
struct _OstreeBlobReaderInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
GBytes *(*read_blob) (OstreeBlobReader *self, GCancellable *cancellable, GError **error);
|
||||
};
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
GBytes *ostree_blob_reader_read_blob (OstreeBlobReader *self, GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
@ -27,6 +27,7 @@
|
||||
#include "otcore.h"
|
||||
#include <libglnx.h>
|
||||
#include <ot-checksum-utils.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "OSTreeSign"
|
||||
@ -152,7 +153,7 @@ ostree_sign_ed25519_data (OstreeSign *self, GBytes *data, GBytes **signature,
|
||||
if (!pkey)
|
||||
{
|
||||
EVP_MD_CTX_free (ctx);
|
||||
return glnx_throw (error, "openssl: Failed to initialize ed5519 key");
|
||||
return glnx_throw (error, "openssl: Failed to initialize ed25519 key");
|
||||
}
|
||||
|
||||
size_t len;
|
||||
@ -320,7 +321,7 @@ ostree_sign_ed25519_clear_keys (OstreeSign *self, GError **error)
|
||||
/* Clear secret key */
|
||||
if (sign->secret_key != NULL)
|
||||
{
|
||||
memset (sign->secret_key, 0, OSTREE_SIGN_ED25519_SECKEY_SIZE);
|
||||
explicit_bzero (sign->secret_key, OSTREE_SIGN_ED25519_SECKEY_SIZE);
|
||||
g_free (sign->secret_key);
|
||||
sign->secret_key = NULL;
|
||||
}
|
||||
@ -451,14 +452,25 @@ _ed25519_add_revoked (OstreeSign *self, GVariant *revoked_key, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
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));
|
||||
|
||||
const gchar *rk_ascii = g_variant_get_string (revoked_key, NULL);
|
||||
g_autofree guint8 *key_owned = NULL;
|
||||
const guint8 *key = NULL;
|
||||
gsize n_elements = 0;
|
||||
g_autofree guint8 *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 = key_owned = g_base64_decode (rk_ascii, &n_elements);
|
||||
}
|
||||
else if (g_variant_is_of_type (revoked_key, G_VARIANT_TYPE_BYTESTRING))
|
||||
{
|
||||
key = g_variant_get_fixed_array (revoked_key, &n_elements, sizeof (guchar));
|
||||
}
|
||||
else
|
||||
{
|
||||
return glnx_throw (error, "Unknown ed25519 revoked key type");
|
||||
}
|
||||
|
||||
if (!validate_length (n_elements, OSTREE_SIGN_ED25519_PUBKEY_SIZE, error))
|
||||
return glnx_prefix_error (error, "Incorrect ed25519 revoked key");
|
||||
@ -477,22 +489,24 @@ _ed25519_add_revoked (OstreeSign *self, GVariant *revoked_key, GError **error)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, gboolean trusted,
|
||||
_load_pk_from_stream (OstreeSign *self, GInputStream *key_stream_in, gboolean trusted,
|
||||
GError **error)
|
||||
{
|
||||
if (key_data_in == NULL)
|
||||
if (key_stream_in == NULL)
|
||||
return glnx_throw (error, "ed25519: unable to read from NULL key-data input stream");
|
||||
|
||||
gboolean ret = FALSE;
|
||||
|
||||
g_autoptr (OstreeBlobReader) blob_reader = ostree_sign_read_pk (self, key_stream_in);
|
||||
g_assert (blob_reader);
|
||||
|
||||
/* Use simple file format with just a list of base64 public keys per line */
|
||||
while (TRUE)
|
||||
{
|
||||
gsize len = 0;
|
||||
g_autoptr (GVariant) pk = NULL;
|
||||
gboolean added = FALSE;
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autofree char *line = g_data_input_stream_read_line (key_data_in, &len, NULL, &local_error);
|
||||
g_autoptr (GBytes) blob = ostree_blob_reader_read_blob (blob_reader, NULL, &local_error);
|
||||
|
||||
if (local_error != NULL)
|
||||
{
|
||||
@ -500,19 +514,20 @@ _load_pk_from_stream (OstreeSign *self, GDataInputStream *key_data_in, gboolean
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (line == NULL)
|
||||
if (blob == NULL)
|
||||
return ret;
|
||||
|
||||
/* Read the key itself */
|
||||
/* base64 encoded key */
|
||||
pk = g_variant_new_string (line);
|
||||
pk = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, blob, FALSE);
|
||||
|
||||
if (trusted)
|
||||
added = ostree_sign_ed25519_add_pk (self, pk, error);
|
||||
else
|
||||
added = _ed25519_add_revoked (self, pk, error);
|
||||
|
||||
g_debug ("%s %s key: %s", added ? "Added" : "Invalid", trusted ? "public" : "revoked", line);
|
||||
g_autofree gchar *pk_printable = g_variant_print (pk, FALSE);
|
||||
g_debug ("%s %s key: %s", added ? "Added" : "Invalid", trusted ? "public" : "revoked",
|
||||
pk_printable);
|
||||
|
||||
/* Mark what we load at least one key */
|
||||
if (added)
|
||||
@ -529,7 +544,6 @@ _load_pk_from_file (OstreeSign *self, const gchar *filename, gboolean trusted, G
|
||||
|
||||
g_autoptr (GFile) keyfile = NULL;
|
||||
g_autoptr (GFileInputStream) key_stream_in = NULL;
|
||||
g_autoptr (GDataInputStream) key_data_in = NULL;
|
||||
|
||||
if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
@ -542,10 +556,7 @@ _load_pk_from_file (OstreeSign *self, const gchar *filename, gboolean trusted, G
|
||||
if (key_stream_in == NULL)
|
||||
return FALSE;
|
||||
|
||||
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, trusted, error))
|
||||
if (!_load_pk_from_stream (self, G_INPUT_STREAM (key_stream_in), trusted, error))
|
||||
{
|
||||
if (error == NULL || *error == NULL)
|
||||
return glnx_throw (error, "signature: ed25519: no valid keys in file '%s'", filename);
|
||||
|
636
src/libostree/ostree-sign-spki.c
Normal file
636
src/libostree/ostree-sign-spki.c
Normal file
@ -0,0 +1,636 @@
|
||||
/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */
|
||||
/*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* Copyright © 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-sign-spki.h"
|
||||
#include "otcore.h"
|
||||
#include <libglnx.h>
|
||||
#include <ot-checksum-utils.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "OSTreeSign"
|
||||
|
||||
#define OSTREE_SIGN_SPKI_NAME "spki"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SPKI_OK,
|
||||
SPKI_NOT_SUPPORTED,
|
||||
SPKI_FAILED_INITIALIZATION
|
||||
} spki_state;
|
||||
|
||||
struct _OstreeSignSpki
|
||||
{
|
||||
GObject parent;
|
||||
spki_state state;
|
||||
GBytes *secret_key;
|
||||
GList *public_keys; /* GBytes */
|
||||
GList *revoked_keys; /* GBytes */
|
||||
};
|
||||
|
||||
static void ostree_sign_spki_iface_init (OstreeSignInterface *self);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (OstreeSignSpki, _ostree_sign_spki, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_SIGN, ostree_sign_spki_iface_init));
|
||||
|
||||
static void
|
||||
ostree_sign_spki_iface_init (OstreeSignInterface *self)
|
||||
{
|
||||
|
||||
self->data = ostree_sign_spki_data;
|
||||
self->data_verify = ostree_sign_spki_data_verify;
|
||||
self->get_name = ostree_sign_spki_get_name;
|
||||
self->metadata_key = ostree_sign_spki_metadata_key;
|
||||
self->metadata_format = ostree_sign_spki_metadata_format;
|
||||
self->clear_keys = ostree_sign_spki_clear_keys;
|
||||
self->set_sk = ostree_sign_spki_set_sk;
|
||||
self->set_pk = ostree_sign_spki_set_pk;
|
||||
self->add_pk = ostree_sign_spki_add_pk;
|
||||
self->load_pk = ostree_sign_spki_load_pk;
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_sign_spki_class_init (OstreeSignSpkiClass *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_sign_spki_init (OstreeSignSpki *self)
|
||||
{
|
||||
|
||||
self->state = SPKI_OK;
|
||||
self->secret_key = NULL;
|
||||
self->public_keys = NULL;
|
||||
self->revoked_keys = NULL;
|
||||
|
||||
#if !defined(USE_OPENSSL)
|
||||
self->state = SPKI_NOT_SUPPORTED;
|
||||
#else
|
||||
if (!otcore_spki_init ())
|
||||
self->state = SPKI_FAILED_INITIALIZATION;
|
||||
#endif
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_sign_spki_is_initialized (OstreeSignSpki *self, GError **error)
|
||||
{
|
||||
switch (self->state)
|
||||
{
|
||||
case SPKI_OK:
|
||||
break;
|
||||
case SPKI_NOT_SUPPORTED:
|
||||
return glnx_throw (error, "spki: engine is not supported");
|
||||
case SPKI_FAILED_INITIALIZATION:
|
||||
return glnx_throw (error, "spki: crypto library isn't initialized properly");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ostree_sign_spki_data (OstreeSign *self, GBytes *data, GBytes **signature,
|
||||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
#if defined(USE_OPENSSL)
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
OstreeSignSpki *sign = _ostree_sign_spki_get_instance_private (OSTREE_SIGN_SPKI (self));
|
||||
|
||||
if (!_ostree_sign_spki_is_initialized (sign, error))
|
||||
return FALSE;
|
||||
|
||||
if (sign->secret_key == NULL)
|
||||
return glnx_throw (error, "Not able to sign: secret key is not set");
|
||||
|
||||
gsize secret_key_size;
|
||||
const guint8 *secret_key_buf = g_bytes_get_data (sign->secret_key, &secret_key_size);
|
||||
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new ();
|
||||
if (!ctx)
|
||||
return glnx_throw (error, "openssl: failed to allocate context");
|
||||
|
||||
const unsigned char *p = secret_key_buf;
|
||||
EVP_PKEY *pkey = d2i_AutoPrivateKey (NULL, &p, secret_key_size);
|
||||
if (!pkey)
|
||||
{
|
||||
EVP_MD_CTX_free (ctx);
|
||||
return glnx_throw (error, "openssl: Failed to initialize spki key");
|
||||
}
|
||||
|
||||
unsigned long long sig_size = 0;
|
||||
g_autofree guchar *sig = NULL;
|
||||
|
||||
size_t len;
|
||||
if (EVP_DigestSignInit (ctx, NULL, NULL, NULL, pkey)
|
||||
&& EVP_DigestSign (ctx, NULL, &len, g_bytes_get_data (data, NULL), g_bytes_get_size (data)))
|
||||
{
|
||||
sig = g_malloc0 (len);
|
||||
if (EVP_DigestSign (ctx, sig, &len, g_bytes_get_data (data, NULL), g_bytes_get_size (data)))
|
||||
sig_size = len;
|
||||
}
|
||||
|
||||
EVP_PKEY_free (pkey);
|
||||
EVP_MD_CTX_free (ctx);
|
||||
|
||||
if (sig_size == 0)
|
||||
return glnx_throw (error, "Failed to sign");
|
||||
|
||||
*signature = g_bytes_new_take (g_steal_pointer (&sig), sig_size);
|
||||
return TRUE;
|
||||
#else
|
||||
return glnx_throw (error, "spki signature validation requested, but support not compiled in");
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
ostree_sign_spki_data_verify (OstreeSign *self, GBytes *data, GVariant *signatures,
|
||||
char **out_success_message, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
if (data == NULL)
|
||||
return glnx_throw (error, "spki: unable to verify NULL data");
|
||||
|
||||
OstreeSignSpki *sign = _ostree_sign_spki_get_instance_private (OSTREE_SIGN_SPKI (self));
|
||||
|
||||
if (!_ostree_sign_spki_is_initialized (sign, error))
|
||||
return FALSE;
|
||||
|
||||
if (signatures == NULL)
|
||||
return glnx_throw (error, "spki: commit have no signatures of my type");
|
||||
|
||||
if (!g_variant_is_of_type (signatures, (GVariantType *)OSTREE_SIGN_METADATA_SPKI_TYPE))
|
||||
return glnx_throw (error, "spki: wrong type passed for verification");
|
||||
|
||||
/* If no keys pre-loaded then,
|
||||
* try to load public keys from storage(s) */
|
||||
if (sign->public_keys == NULL)
|
||||
{
|
||||
g_autoptr (GVariantBuilder) builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
g_autoptr (GVariant) options = g_variant_builder_end (builder);
|
||||
|
||||
if (!ostree_sign_spki_load_pk (self, options, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_debug ("verify: data hash = 0x%x", g_bytes_hash (data));
|
||||
|
||||
g_autoptr (GString) invalid_signatures = NULL;
|
||||
guint n_invalid_signatures = 0;
|
||||
|
||||
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_debug ("Read signature %d: %s", (gint)i, g_variant_print (child, TRUE));
|
||||
|
||||
for (GList *l = sign->public_keys; l != NULL; l = l->next)
|
||||
{
|
||||
GBytes *public_key = l->data;
|
||||
/* TODO: use non-list for tons of revoked keys? */
|
||||
if (g_list_find_custom (sign->revoked_keys, public_key, g_bytes_compare) != NULL)
|
||||
{
|
||||
g_autofree char *hex = g_malloc0 (g_bytes_get_size (public_key) * 2 + 1);
|
||||
ot_bin2hex (hex, g_bytes_get_data (public_key, NULL), g_bytes_get_size (public_key));
|
||||
g_debug ("Skip revoked key '%s'", hex);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool valid = false;
|
||||
if (!otcore_validate_spki_signature (data, public_key, signature, &valid, error))
|
||||
return FALSE;
|
||||
if (!valid)
|
||||
{
|
||||
/* Incorrect signature! */
|
||||
if (invalid_signatures == NULL)
|
||||
invalid_signatures = g_string_new ("");
|
||||
else
|
||||
g_string_append (invalid_signatures, "; ");
|
||||
n_invalid_signatures++;
|
||||
g_autofree char *hex = g_malloc0 (g_bytes_get_size (public_key) * 2 + 1);
|
||||
ot_bin2hex (hex, g_bytes_get_data (public_key, NULL), g_bytes_get_size (public_key));
|
||||
g_string_append_printf (invalid_signatures, "key '%s'", hex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out_success_message)
|
||||
{
|
||||
g_autofree char *hex = g_malloc0 (g_bytes_get_size (public_key) * 2 + 1);
|
||||
ot_bin2hex (hex, g_bytes_get_data (public_key, NULL),
|
||||
g_bytes_get_size (public_key));
|
||||
*out_success_message = g_strdup_printf (
|
||||
"spki: Signature verified successfully with key '%s'", hex);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid_signatures)
|
||||
{
|
||||
g_assert_cmpuint (n_invalid_signatures, >, 0);
|
||||
/* The test suite has a key ring with 100 keys. This seems insane, let's
|
||||
* cap a reasonable error message at 3.
|
||||
*/
|
||||
if (n_invalid_signatures > 3)
|
||||
return glnx_throw (error, "spki: Signature couldn't be verified; tried %u keys",
|
||||
n_invalid_signatures);
|
||||
return glnx_throw (error, "spki: Signature couldn't be verified with: %s",
|
||||
invalid_signatures->str);
|
||||
}
|
||||
return glnx_throw (error, "spki: no signatures found");
|
||||
}
|
||||
|
||||
const gchar *
|
||||
ostree_sign_spki_get_name (OstreeSign *self)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
return OSTREE_SIGN_SPKI_NAME;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
ostree_sign_spki_metadata_key (OstreeSign *self)
|
||||
{
|
||||
|
||||
return OSTREE_SIGN_METADATA_SPKI_KEY;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
ostree_sign_spki_metadata_format (OstreeSign *self)
|
||||
{
|
||||
|
||||
return OSTREE_SIGN_METADATA_SPKI_TYPE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ostree_sign_spki_clear_keys (OstreeSign *self, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
OstreeSignSpki *sign = _ostree_sign_spki_get_instance_private (OSTREE_SIGN_SPKI (self));
|
||||
|
||||
if (!_ostree_sign_spki_is_initialized (sign, error))
|
||||
return FALSE;
|
||||
|
||||
/* Clear secret key */
|
||||
if (sign->secret_key != NULL)
|
||||
{
|
||||
gsize size;
|
||||
gpointer data = g_bytes_unref_to_data (sign->secret_key, &size);
|
||||
explicit_bzero (data, size);
|
||||
sign->secret_key = NULL;
|
||||
}
|
||||
|
||||
/* Clear already loaded trusted keys */
|
||||
if (sign->public_keys != NULL)
|
||||
{
|
||||
g_list_free_full (sign->public_keys, (GDestroyNotify)g_bytes_unref);
|
||||
sign->public_keys = NULL;
|
||||
}
|
||||
|
||||
/* Clear already loaded revoked keys */
|
||||
if (sign->revoked_keys != NULL)
|
||||
{
|
||||
g_list_free_full (sign->revoked_keys, (GDestroyNotify)g_bytes_unref);
|
||||
sign->revoked_keys = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Support 2 representations:
|
||||
* base64 ascii -- secret key is passed as string
|
||||
* raw key -- key is passed as bytes array
|
||||
* */
|
||||
gboolean
|
||||
ostree_sign_spki_set_sk (OstreeSign *self, GVariant *secret_key, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
if (!ostree_sign_spki_clear_keys (self, error))
|
||||
return FALSE;
|
||||
|
||||
OstreeSignSpki *sign = _ostree_sign_spki_get_instance_private (OSTREE_SIGN_SPKI (self));
|
||||
|
||||
gsize n_elements = 0;
|
||||
|
||||
g_autofree guchar *secret_key_buf = NULL;
|
||||
if (g_variant_is_of_type (secret_key, G_VARIANT_TYPE_STRING))
|
||||
{
|
||||
const gchar *sk_ascii = g_variant_get_string (secret_key, NULL);
|
||||
secret_key_buf = g_base64_decode (sk_ascii, &n_elements);
|
||||
}
|
||||
else if (g_variant_is_of_type (secret_key, G_VARIANT_TYPE_BYTESTRING))
|
||||
{
|
||||
secret_key_buf
|
||||
= (guchar *)g_variant_get_fixed_array (secret_key, &n_elements, sizeof (guchar));
|
||||
}
|
||||
else
|
||||
{
|
||||
return glnx_throw (error, "Unknown spki secret key type");
|
||||
}
|
||||
|
||||
sign->secret_key = g_bytes_new_take (g_steal_pointer (&secret_key_buf), n_elements);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Support 2 representations:
|
||||
* base64 ascii -- public key is passed as string
|
||||
* raw key -- key is passed as bytes array
|
||||
* */
|
||||
gboolean
|
||||
ostree_sign_spki_set_pk (OstreeSign *self, GVariant *public_key, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
if (!ostree_sign_spki_clear_keys (self, error))
|
||||
return FALSE;
|
||||
|
||||
return ostree_sign_spki_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_spki_add_pk (OstreeSign *self, GVariant *public_key, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
OstreeSignSpki *sign = _ostree_sign_spki_get_instance_private (OSTREE_SIGN_SPKI (self));
|
||||
|
||||
if (!_ostree_sign_spki_is_initialized (sign, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree guint8 *key_owned = NULL;
|
||||
const guint8 *key = NULL;
|
||||
gsize n_elements = 0;
|
||||
|
||||
if (g_variant_is_of_type (public_key, G_VARIANT_TYPE_STRING))
|
||||
{
|
||||
const gchar *pk_ascii = g_variant_get_string (public_key, NULL);
|
||||
key = key_owned = g_base64_decode (pk_ascii, &n_elements);
|
||||
}
|
||||
else if (g_variant_is_of_type (public_key, G_VARIANT_TYPE_BYTESTRING))
|
||||
{
|
||||
key = g_variant_get_fixed_array (public_key, &n_elements, sizeof (guchar));
|
||||
}
|
||||
else
|
||||
{
|
||||
return glnx_throw (error, "Unknown spki public key type");
|
||||
}
|
||||
|
||||
g_autofree char *hex = g_malloc0 (n_elements * 2 + 1);
|
||||
ot_bin2hex (hex, key, n_elements);
|
||||
g_debug ("Read spki public key = %s", hex);
|
||||
|
||||
g_autoptr (GBytes) key_bytes = g_bytes_new_static (key, n_elements);
|
||||
if (g_list_find_custom (sign->public_keys, key_bytes, g_bytes_compare) == NULL)
|
||||
{
|
||||
GBytes *new_key_bytes = g_bytes_new (key, n_elements);
|
||||
sign->public_keys = g_list_prepend (sign->public_keys, new_key_bytes);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Add revoked public key */
|
||||
static gboolean
|
||||
_spki_add_revoked (OstreeSign *self, GVariant *revoked_key, GError **error)
|
||||
{
|
||||
g_assert (OSTREE_IS_SIGN (self));
|
||||
|
||||
OstreeSignSpki *sign = _ostree_sign_spki_get_instance_private (OSTREE_SIGN_SPKI (self));
|
||||
|
||||
g_autofree guint8 *key_owned = NULL;
|
||||
const guint8 *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 = key_owned = g_base64_decode (rk_ascii, &n_elements);
|
||||
}
|
||||
else if (g_variant_is_of_type (revoked_key, G_VARIANT_TYPE_BYTESTRING))
|
||||
{
|
||||
key = g_variant_get_fixed_array (revoked_key, &n_elements, sizeof (guchar));
|
||||
}
|
||||
else
|
||||
{
|
||||
return glnx_throw (error, "Unknown spki revoked key type");
|
||||
}
|
||||
|
||||
g_autofree char *hex = g_malloc0 (n_elements * 2 + 1);
|
||||
ot_bin2hex (hex, key, n_elements);
|
||||
g_debug ("Read spki revoked key = %s", hex);
|
||||
|
||||
g_autoptr (GBytes) key_bytes = g_bytes_new_static (key, n_elements);
|
||||
if (g_list_find_custom (sign->revoked_keys, key, g_bytes_compare) == NULL)
|
||||
{
|
||||
GBytes *new_key_bytes = g_bytes_new (key, n_elements);
|
||||
sign->revoked_keys = g_list_prepend (sign->revoked_keys, new_key_bytes);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_load_pk_from_stream (OstreeSign *self, GInputStream *key_stream_in, gboolean trusted,
|
||||
GError **error)
|
||||
{
|
||||
if (key_stream_in == NULL)
|
||||
return glnx_throw (error, "spki: unable to read from NULL key-data input stream");
|
||||
|
||||
gboolean ret = FALSE;
|
||||
|
||||
g_autoptr (OstreeBlobReader) blob_reader = ostree_sign_read_pk (self, key_stream_in);
|
||||
g_assert (blob_reader);
|
||||
|
||||
/* Use simple file format with just a list of base64 public keys per line */
|
||||
while (TRUE)
|
||||
{
|
||||
gboolean added = FALSE;
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autoptr (GBytes) blob = ostree_blob_reader_read_blob (blob_reader, NULL, &local_error);
|
||||
|
||||
if (local_error != NULL)
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (blob == NULL)
|
||||
return ret;
|
||||
|
||||
/* Read the key itself */
|
||||
g_autoptr (GVariant) pk = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, blob, FALSE);
|
||||
|
||||
if (trusted)
|
||||
added = ostree_sign_spki_add_pk (self, pk, error);
|
||||
else
|
||||
added = _spki_add_revoked (self, pk, error);
|
||||
|
||||
g_autofree gchar *pk_printable = g_variant_print (pk, FALSE);
|
||||
g_debug ("%s %s key: %s", added ? "Added" : "Invalid", trusted ? "public" : "revoked",
|
||||
pk_printable);
|
||||
|
||||
/* Mark what we load at least one key */
|
||||
if (added)
|
||||
ret = TRUE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_load_pk_from_file (OstreeSign *self, const gchar *filename, gboolean trusted, GError **error)
|
||||
{
|
||||
g_debug ("Processing file '%s'", filename);
|
||||
|
||||
if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
g_debug ("Can't open file '%s' with public keys", filename);
|
||||
return glnx_throw (error, "File object '%s' is not a regular file", filename);
|
||||
}
|
||||
|
||||
g_autoptr (GFile) keyfile = keyfile = g_file_new_for_path (filename);
|
||||
g_autoptr (GFileInputStream) key_stream_in = g_file_read (keyfile, NULL, error);
|
||||
if (key_stream_in == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!_load_pk_from_stream (self, G_INPUT_STREAM (key_stream_in), trusted, error))
|
||||
{
|
||||
if (error == NULL || *error == NULL)
|
||||
return glnx_throw (error, "signature: spki: no valid keys in file '%s'", filename);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_spki_load_pk (OstreeSign *self, GVariant *options, gboolean trusted, GError **error)
|
||||
{
|
||||
|
||||
gboolean ret = FALSE;
|
||||
const gchar *custom_dir = NULL;
|
||||
|
||||
g_autoptr (GPtrArray) base_dirs = g_ptr_array_new_with_free_func (g_free);
|
||||
g_autoptr (GPtrArray) spki_files = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
if (g_variant_lookup (options, "basedir", "&s", &custom_dir))
|
||||
{
|
||||
/* Add custom directory */
|
||||
g_ptr_array_add (base_dirs, g_strdup (custom_dir));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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"));
|
||||
}
|
||||
|
||||
/* 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_autofree gchar *base_name
|
||||
= g_build_filename ((gchar *)g_ptr_array_index (base_dirs, i),
|
||||
trusted ? "trusted.spki" : "revoked.spki", NULL);
|
||||
|
||||
g_debug ("Check spki keys from file: %s", base_name);
|
||||
g_autofree gchar *base_dir = g_strconcat (base_name, ".d", NULL);
|
||||
g_ptr_array_add (spki_files, g_steal_pointer (&base_name));
|
||||
|
||||
g_autoptr (GDir) dir = g_dir_open (base_dir, 0, error);
|
||||
if (dir == NULL)
|
||||
{
|
||||
g_clear_error (error);
|
||||
continue;
|
||||
}
|
||||
const gchar *entry = NULL;
|
||||
while ((entry = g_dir_read_name (dir)) != NULL)
|
||||
{
|
||||
gchar *filename = g_build_filename (base_dir, entry, NULL);
|
||||
g_debug ("Check spki keys from file: %s", filename);
|
||||
g_ptr_array_add (spki_files, filename);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan all well-known files */
|
||||
for (gint i = 0; i < spki_files->len; i++)
|
||||
{
|
||||
if (!_load_pk_from_file (self, (gchar *)g_ptr_array_index (spki_files, i), trusted, error))
|
||||
{
|
||||
g_debug ("Problem with loading spki %s keys from `%s`", trusted ? "public" : "revoked",
|
||||
(gchar *)g_ptr_array_index (spki_files, i));
|
||||
g_clear_error (error);
|
||||
}
|
||||
else
|
||||
ret = TRUE;
|
||||
}
|
||||
|
||||
if (!ret && (error == NULL || *error == NULL))
|
||||
return glnx_throw (error, "signature: spki: 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.spki.d' and 'revoked.spki.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_spki_load_pk (OstreeSign *self, GVariant *options, GError **error)
|
||||
{
|
||||
|
||||
const gchar *filename = NULL;
|
||||
|
||||
OstreeSignSpki *sign = _ostree_sign_spki_get_instance_private (OSTREE_SIGN_SPKI (self));
|
||||
if (!_ostree_sign_spki_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);
|
||||
|
||||
/* Load public keys from well-known directories and files */
|
||||
if (!_spki_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 (!_spki_load_pk (self, options, FALSE, error))
|
||||
g_clear_error (error);
|
||||
|
||||
return TRUE;
|
||||
}
|
54
src/libostree/ostree-sign-spki.h
Normal file
54
src/libostree/ostree-sign-spki.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* vim:set et sw=2 cin cino=t0,f0,(0,{s,>2s,n-s,^-s,e2s: */
|
||||
|
||||
/*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* Copyright © 2024 Red Hat, Inc.
|
||||
*
|
||||
* 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 "ostree-sign.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_SIGN_SPKI (_ostree_sign_spki_get_type ())
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_FINAL_TYPE (OstreeSignSpki, _ostree_sign_spki, OSTREE, SIGN_SPKI, GObject)
|
||||
|
||||
gboolean ostree_sign_spki_data (OstreeSign *self, GBytes *data, GBytes **signature,
|
||||
GCancellable *cancellable, GError **error);
|
||||
|
||||
gboolean ostree_sign_spki_data_verify (OstreeSign *self, GBytes *data, GVariant *signatures,
|
||||
char **out_success_message, GError **error);
|
||||
|
||||
const gchar *ostree_sign_spki_get_name (OstreeSign *self);
|
||||
const gchar *ostree_sign_spki_metadata_key (OstreeSign *self);
|
||||
const gchar *ostree_sign_spki_metadata_format (OstreeSign *self);
|
||||
|
||||
gboolean ostree_sign_spki_clear_keys (OstreeSign *self, GError **error);
|
||||
|
||||
gboolean ostree_sign_spki_set_sk (OstreeSign *self, GVariant *secret_key, GError **error);
|
||||
|
||||
gboolean ostree_sign_spki_set_pk (OstreeSign *self, GVariant *public_key, GError **error);
|
||||
|
||||
gboolean ostree_sign_spki_add_pk (OstreeSign *self, GVariant *public_key, GError **error);
|
||||
|
||||
gboolean ostree_sign_spki_load_pk (OstreeSign *self, GVariant *options, GError **error);
|
||||
|
||||
G_END_DECLS
|
@ -38,10 +38,14 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-blob-reader-base64.h"
|
||||
#include "ostree-blob-reader-pem.h"
|
||||
#include "ostree-blob-reader-raw.h"
|
||||
#include "ostree-core.h"
|
||||
#include "ostree-sign-dummy.h"
|
||||
#include "ostree-sign-ed25519.h"
|
||||
#include "ostree-sign-private.h"
|
||||
#include "ostree-sign-spki.h"
|
||||
#include "ostree-sign.h"
|
||||
|
||||
#include "ostree-autocleanups.h"
|
||||
@ -59,6 +63,9 @@ typedef struct
|
||||
_sign_type sign_types[] = {
|
||||
#if defined(HAVE_ED25519)
|
||||
{ OSTREE_SIGN_NAME_ED25519, 0 },
|
||||
#endif
|
||||
#if defined(HAVE_SPKI)
|
||||
{ OSTREE_SIGN_NAME_SPKI, 0 },
|
||||
#endif
|
||||
{ "dummy", 0 }
|
||||
};
|
||||
@ -67,6 +74,9 @@ enum
|
||||
{
|
||||
#if defined(HAVE_ED25519)
|
||||
SIGN_ED25519,
|
||||
#endif
|
||||
#if defined(HAVE_SPKI)
|
||||
SIGN_SPKI,
|
||||
#endif
|
||||
SIGN_DUMMY
|
||||
};
|
||||
@ -536,6 +546,10 @@ ostree_sign_get_by_name (const gchar *name, GError **error)
|
||||
#if defined(HAVE_ED25519)
|
||||
if (sign_types[SIGN_ED25519].type == 0)
|
||||
sign_types[SIGN_ED25519].type = OSTREE_TYPE_SIGN_ED25519;
|
||||
#endif
|
||||
#if defined(HAVE_SPKI)
|
||||
if (sign_types[SIGN_SPKI].type == 0)
|
||||
sign_types[SIGN_SPKI].type = OSTREE_TYPE_SIGN_SPKI;
|
||||
#endif
|
||||
if (sign_types[SIGN_DUMMY].type == 0)
|
||||
sign_types[SIGN_DUMMY].type = OSTREE_TYPE_SIGN_DUMMY;
|
||||
@ -641,3 +655,57 @@ ostree_sign_summary (OstreeSign *self, OstreeRepo *repo, GVariant *keys, GCancel
|
||||
{
|
||||
return _ostree_sign_summary_at (self, repo, repo->repo_dir_fd, keys, cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sign_read_pk:
|
||||
* @self: Self
|
||||
* @stream: a #GInputStream
|
||||
*
|
||||
* Start reading public keys from a stream.
|
||||
*
|
||||
* Returns: (transfer full): a #OstreamBlobReader or %NULL on error
|
||||
*
|
||||
* Since: 2024.8
|
||||
*/
|
||||
OstreeBlobReader *
|
||||
ostree_sign_read_pk (OstreeSign *self, GInputStream *stream)
|
||||
{
|
||||
#if defined(HAVE_ED25519)
|
||||
if (OSTREE_IS_SIGN_ED25519 (self))
|
||||
return OSTREE_BLOB_READER (_ostree_blob_reader_base64_new (stream));
|
||||
#endif
|
||||
#if defined(HAVE_SPKI)
|
||||
if (OSTREE_IS_SIGN_SPKI (self))
|
||||
return OSTREE_BLOB_READER (_ostree_blob_reader_pem_new (stream, "PUBLIC KEY"));
|
||||
#endif
|
||||
if (OSTREE_IS_SIGN_DUMMY (self))
|
||||
return OSTREE_BLOB_READER (_ostree_blob_reader_raw_new (stream));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sign_read_sk:
|
||||
* @self: Self
|
||||
* @stream: a #GInputStream
|
||||
*
|
||||
* Start reading secret keys from a stream.
|
||||
*
|
||||
* Returns: (transfer full): a #OstreamBlobReader or %NULL on error
|
||||
*
|
||||
* Since: 2024.8
|
||||
*/
|
||||
OstreeBlobReader *
|
||||
ostree_sign_read_sk (OstreeSign *self, GInputStream *stream)
|
||||
{
|
||||
#if defined(HAVE_ED25519)
|
||||
if (OSTREE_IS_SIGN_ED25519 (self))
|
||||
return OSTREE_BLOB_READER (_ostree_blob_reader_base64_new (stream));
|
||||
#endif
|
||||
#if defined(HAVE_SPKI)
|
||||
if (OSTREE_IS_SIGN_SPKI (self))
|
||||
return OSTREE_BLOB_READER (_ostree_blob_reader_pem_new (stream, "PRIVATE KEY"));
|
||||
#endif
|
||||
if (OSTREE_IS_SIGN_DUMMY (self))
|
||||
return OSTREE_BLOB_READER (_ostree_blob_reader_raw_new (stream));
|
||||
return NULL;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "ostree-blob-reader.h"
|
||||
#include "ostree-ref.h"
|
||||
#include "ostree-remote.h"
|
||||
#include "ostree-types.h"
|
||||
@ -43,6 +44,14 @@ G_BEGIN_DECLS
|
||||
*/
|
||||
#define OSTREE_SIGN_NAME_ED25519 "ed25519"
|
||||
|
||||
/**
|
||||
* OSTREE_SIGN_NAME_SPKI:
|
||||
* The name of the spki signing type.
|
||||
*
|
||||
* Since: 2024.7
|
||||
*/
|
||||
#define OSTREE_SIGN_NAME_SPKI "spki"
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
G_DECLARE_INTERFACE (OstreeSign, ostree_sign, OSTREE, SIGN, GObject)
|
||||
|
||||
@ -113,4 +122,11 @@ 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);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeBlobReader *ostree_sign_read_pk (OstreeSign *self, GInputStream *stream);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
OstreeBlobReader *ostree_sign_read_sk (OstreeSign *self, GInputStream *stream);
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -97,7 +97,7 @@ otcore_validate_ed25519_signature (GBytes *data, GBytes *public_key, GBytes *sig
|
||||
if (!pkey)
|
||||
{
|
||||
EVP_MD_CTX_free (ctx);
|
||||
return glnx_throw (error, "openssl: Failed to initialize ed5519 key");
|
||||
return glnx_throw (error, "openssl: Failed to initialize ed25519 key");
|
||||
}
|
||||
if (EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, pkey) != 0
|
||||
&& EVP_DigestVerify (ctx, signature_buf, OSTREE_SIGN_ED25519_SIG_SIZE,
|
||||
|
89
src/libotcore/otcore-spki-verify.c
Normal file
89
src/libotcore/otcore-spki-verify.c
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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_spki_init (void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Validate a single spki 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_spki_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_OPENSSL)
|
||||
gsize public_key_size;
|
||||
const guint8 *public_key_buf = g_bytes_get_data (public_key, &public_key_size);
|
||||
|
||||
gsize signature_size;
|
||||
const guint8 *signature_buf = g_bytes_get_data (signature, &signature_size);
|
||||
|
||||
if (public_key_size > OSTREE_SIGN_MAX_METADATA_SIZE)
|
||||
return glnx_throw (
|
||||
error, "Invalid public key of %" G_GSIZE_FORMAT " bytes, expected <= %" G_GSIZE_FORMAT,
|
||||
public_key_size, (gsize)OSTREE_SIGN_MAX_METADATA_SIZE);
|
||||
|
||||
if (signature_size > OSTREE_SIGN_MAX_METADATA_SIZE)
|
||||
return glnx_throw (
|
||||
error, "Invalid signature of %" G_GSIZE_FORMAT " bytes, expected <= %" G_GSIZE_FORMAT,
|
||||
signature_size, (gsize)OSTREE_SIGN_MAX_METADATA_SIZE);
|
||||
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new ();
|
||||
if (!ctx)
|
||||
return glnx_throw (error, "openssl: failed to allocate context");
|
||||
|
||||
const unsigned char *p = public_key_buf;
|
||||
EVP_PKEY *pkey = d2i_PUBKEY (NULL, &p, public_key_size);
|
||||
if (!pkey)
|
||||
{
|
||||
EVP_MD_CTX_free (ctx);
|
||||
return glnx_throw (error, "openssl: Failed to initialize spki key");
|
||||
}
|
||||
if (EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, pkey) != 0
|
||||
&& EVP_DigestVerify (ctx, signature_buf, signature_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, "spki signature validation requested, but support not compiled in");
|
||||
#endif
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
#define USE_LIBSODIUM
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/x509.h>
|
||||
#define USE_OPENSSL
|
||||
#endif
|
||||
|
||||
@ -39,10 +40,22 @@
|
||||
// The variant type
|
||||
#define OSTREE_SIGN_METADATA_ED25519_TYPE "aay"
|
||||
|
||||
// This key is stored inside commit metadata.
|
||||
#define OSTREE_SIGN_METADATA_SPKI_KEY "ostree.sign.spki"
|
||||
// The variant type
|
||||
#define OSTREE_SIGN_METADATA_SPKI_TYPE "aay"
|
||||
|
||||
// Maximum size of metadata in bytes, in sync with OSTREE_MAX_METADATA_SIZE
|
||||
#define OSTREE_SIGN_MAX_METADATA_SIZE (128 * 1024 * 1024)
|
||||
|
||||
bool otcore_ed25519_init (void);
|
||||
gboolean otcore_validate_ed25519_signature (GBytes *data, GBytes *pubkey, GBytes *signature,
|
||||
bool *out_valid, GError **error);
|
||||
|
||||
bool otcore_spki_init (void);
|
||||
gboolean otcore_validate_spki_signature (GBytes *data, GBytes *public_key, GBytes *signature,
|
||||
bool *out_valid, GError **error);
|
||||
|
||||
char *otcore_find_proc_cmdline_key (const char *cmdline, const char *key);
|
||||
gboolean otcore_get_ostree_target (const char *cmdline, gboolean *is_aboot, char **out_target,
|
||||
GError **error);
|
||||
|
@ -961,19 +961,43 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Load each base64 encoded private key in a file and sign with it.
|
||||
// Load each encoded private key in a file and sign with it.
|
||||
for (char **iter = opt_key_files; iter && *iter; iter++)
|
||||
{
|
||||
const char *path = *iter;
|
||||
g_autofree char *b64key
|
||||
= glnx_file_get_contents_utf8_at (AT_FDCWD, path, NULL, NULL, error);
|
||||
if (!b64key)
|
||||
g_autoptr (GFile) keyfile = NULL;
|
||||
g_autoptr (GFileInputStream) key_stream_in = NULL;
|
||||
g_autoptr (OstreeBlobReader) blob_reader = NULL;
|
||||
g_autoptr (GBytes) blob = NULL;
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autoptr (GVariant) secret_key = NULL;
|
||||
|
||||
keyfile = g_file_new_for_path (path);
|
||||
key_stream_in = g_file_read (keyfile, NULL, error);
|
||||
if (key_stream_in == NULL)
|
||||
goto out;
|
||||
|
||||
g_assert (sign);
|
||||
blob_reader = ostree_sign_read_sk (sign, G_INPUT_STREAM (key_stream_in));
|
||||
if (blob_reader == NULL)
|
||||
goto out;
|
||||
|
||||
blob = ostree_blob_reader_read_blob (blob_reader, cancellable, &local_error);
|
||||
if (local_error != NULL)
|
||||
{
|
||||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error), "Reading %s",
|
||||
path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (blob == NULL)
|
||||
{
|
||||
g_prefix_error (error, "Reading %s", path);
|
||||
goto out;
|
||||
}
|
||||
g_autoptr (GVariant) secret_key = g_variant_new_string (b64key);
|
||||
g_assert (sign);
|
||||
|
||||
// Pass the key as a bytestring
|
||||
secret_key = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, blob, FALSE);
|
||||
if (!ostree_sign_set_sk (sign, secret_key, error))
|
||||
goto out;
|
||||
|
||||
|
@ -188,7 +188,7 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation,
|
||||
{
|
||||
g_autoptr (GFile) keyfile = NULL;
|
||||
g_autoptr (GFileInputStream) key_stream_in = NULL;
|
||||
g_autoptr (GDataInputStream) key_data_in = NULL;
|
||||
g_autoptr (OstreeBlobReader) blob_reader = NULL;
|
||||
|
||||
if (!g_file_test (opt_filename, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
@ -203,25 +203,24 @@ ostree_builtin_sign (int argc, char **argv, OstreeCommandInvocation *invocation,
|
||||
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);
|
||||
blob_reader = ostree_sign_read_sk (sign, G_INPUT_STREAM (key_stream_in));
|
||||
g_assert (blob_reader != 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 (GBytes) blob
|
||||
= ostree_blob_reader_read_blob (blob_reader, cancellable, error);
|
||||
g_autoptr (GVariant) sk = NULL;
|
||||
|
||||
if (*error != NULL)
|
||||
goto out;
|
||||
|
||||
if (line == NULL)
|
||||
if (blob == NULL)
|
||||
break;
|
||||
|
||||
// Pass the key as a string
|
||||
sk = g_variant_new_string (line);
|
||||
sk = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, blob, FALSE);
|
||||
if (!ostree_sign_set_sk (sign, sk, error))
|
||||
{
|
||||
ret = FALSE;
|
||||
|
@ -780,6 +780,40 @@ gen_ed25519_random_public()
|
||||
openssl genpkey -algorithm ED25519 | openssl pkey -outform DER | tail -c 32 | base64
|
||||
}
|
||||
|
||||
# Keys for spki signing tests
|
||||
SPKIPUBLICPEM=
|
||||
SPKISECRETPEM=
|
||||
|
||||
SPKIPUBLIC=
|
||||
SPKISECRET=
|
||||
|
||||
gen_spki_keys ()
|
||||
{
|
||||
# Generate private key in PEM format
|
||||
SPKISECRETPEM="$(mktemp -p ${test_tmpdir} ed448_sk_XXXXXX.pem)"
|
||||
openssl genpkey -algorithm ed448 -outform PEM -out "${SPKISECRETPEM}"
|
||||
SPKIPUBLICPEM="$(mktemp -p ${test_tmpdir} ed448_pk_XXXXXX.pem)"
|
||||
openssl pkey -outform PEM -pubout -in "${SPKISECRETPEM}" -out "${SPKIPUBLICPEM}"
|
||||
|
||||
SPKIPUBLIC="$(openssl pkey -inform PEM -outform DER -pubin -pubout -in ${SPKIPUBLICPEM} | base64 -w 0)"
|
||||
SPKISECRET="$(openssl pkey -inform PEM -outform DER -in ${SPKISECRETPEM} | base64 -w 0)"
|
||||
|
||||
echo "Generated ed448 keys:"
|
||||
echo "public: ${SPKIPUBLIC}"
|
||||
echo "secret: ${SPKISECRET}"
|
||||
}
|
||||
|
||||
gen_spki_random_public()
|
||||
{
|
||||
openssl genpkey -algorithm ed448 | openssl pkey -pubout -outform DER | base64 -w 0
|
||||
echo
|
||||
}
|
||||
|
||||
gen_spki_random_public_pem()
|
||||
{
|
||||
openssl genpkey -algorithm ed448 | openssl pkey -pubout -outform PEM
|
||||
}
|
||||
|
||||
is_bare_user_only_repo () {
|
||||
grep -q 'mode=bare-user-only' $1/config
|
||||
}
|
||||
|
124
tests/test-pem.c
Normal file
124
tests/test-pem.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 <gio/gio.h>
|
||||
|
||||
#include "ostree-blob-reader-private.h"
|
||||
|
||||
static const guint8 pubkey_ed25519[]
|
||||
= { 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0x36, 0x09, 0x06,
|
||||
0x69, 0xf3, 0x52, 0xb1, 0xe3, 0x7e, 0xd4, 0xb5, 0xe3, 0x4c, 0x52, 0x6b, 0x7d, 0xdb, 0xba,
|
||||
0x37, 0x6a, 0xac, 0xe6, 0xb9, 0x5f, 0xf5, 0xdd, 0xf1, 0x95, 0xa5, 0x5c, 0x96, 0x09 };
|
||||
|
||||
static const gchar pem_pubkey_ed25519[]
|
||||
= "-----BEGIN PUBLIC KEY-----\n"
|
||||
"MCowBQYDK2VwAyEANgkGafNSseN+1LXjTFJrfdu6N2qs5rlf9d3xlaVclgk=\n"
|
||||
"-----END PUBLIC KEY-----\n";
|
||||
|
||||
static const gchar pem_pubkey_ed25519_whitespace[]
|
||||
= "-----BEGIN PUBLIC KEY-----\n"
|
||||
" \n"
|
||||
"MCowBQYDK2VwAyEANgkGafNSseN+1LXjTFJrfdu6N2qs5rlf9d3xlaVclgk=\n"
|
||||
"-----END PUBLIC KEY-----\n";
|
||||
|
||||
static const gchar pem_pubkey_empty[] = "";
|
||||
|
||||
static const gchar pem_pubkey_ed25519_no_trailer[]
|
||||
= "-----BEGIN PUBLIC KEY-----\n"
|
||||
"MCowBQYDK2VwAyEANgkGafNSseN+1LXjTFJrfdu6N2qs5rlf9d3xlaVclgk=\n";
|
||||
|
||||
static const gchar pem_pubkey_ed25519_label_mismatch[]
|
||||
= "-----BEGIN PUBLIC KEY X-----\n"
|
||||
"MCowBQYDK2VwAyEANgkGafNSseN+1LXjTFJrfdu6N2qs5rlf9d3xlaVclgk=\n"
|
||||
"-----END PUBLIC KEY Y-----\n";
|
||||
|
||||
static void
|
||||
test_ostree_read_pem_block_valid (void)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const gchar *pem_data;
|
||||
gsize pem_size;
|
||||
const gchar *label;
|
||||
const guint8 *data;
|
||||
gsize size;
|
||||
} tests[] = {
|
||||
{ pem_pubkey_ed25519, sizeof (pem_pubkey_ed25519), "PUBLIC KEY", pubkey_ed25519,
|
||||
sizeof (pubkey_ed25519) },
|
||||
{ pem_pubkey_ed25519_whitespace, sizeof (pem_pubkey_ed25519_whitespace), "PUBLIC KEY",
|
||||
pubkey_ed25519, sizeof (pubkey_ed25519) },
|
||||
{ pem_pubkey_empty, sizeof (pem_pubkey_empty), NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
for (gsize i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
g_autoptr (GInputStream) stream
|
||||
= g_memory_input_stream_new_from_data (tests[i].pem_data, tests[i].pem_size, NULL);
|
||||
g_autoptr (GDataInputStream) data_stream = g_data_input_stream_new (stream);
|
||||
|
||||
g_autofree char *label = NULL;
|
||||
g_autoptr (GBytes) bytes = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
bytes = _ostree_read_pem_block (data_stream, &label, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_cmpstr (label, ==, tests[i].label);
|
||||
if (tests[i].data)
|
||||
{
|
||||
g_autoptr (GBytes) expected_bytes = g_bytes_new_static (tests[i].data, tests[i].size);
|
||||
g_assert (g_bytes_equal (bytes, expected_bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_ostree_read_pem_block_invalid (void)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const gchar *pem_data;
|
||||
gsize pem_size;
|
||||
} tests[] = {
|
||||
{ pem_pubkey_ed25519_no_trailer, sizeof (pem_pubkey_ed25519_no_trailer) },
|
||||
{ pem_pubkey_ed25519_label_mismatch, sizeof (pem_pubkey_ed25519_label_mismatch) },
|
||||
};
|
||||
|
||||
for (gsize i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
g_autoptr (GInputStream) stream
|
||||
= g_memory_input_stream_new_from_data (tests[i].pem_data, tests[i].pem_size, NULL);
|
||||
g_autoptr (GDataInputStream) data_stream = g_data_input_stream_new (stream);
|
||||
|
||||
g_autofree char *label = NULL;
|
||||
g_autoptr (GBytes) bytes = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
bytes = _ostree_read_pem_block (data_stream, &label, NULL, &error);
|
||||
g_assert_null (bytes);
|
||||
g_assert_null (label);
|
||||
g_assert_nonnull (error);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/ostree_read_pem_block/valid", test_ostree_read_pem_block_valid);
|
||||
g_test_add_func ("/ostree_read_pem_block/invalid", test_ostree_read_pem_block_invalid);
|
||||
return g_test_run ();
|
||||
}
|
61
tests/test-signed-commit-dummy.sh
Executable file
61
tests/test-signed-commit-dummy.sh
Executable file
@ -0,0 +1,61 @@
|
||||
#!/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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
. $(dirname $0)/libtest.sh
|
||||
|
||||
# This is explicitly opt in for testing
|
||||
export OSTREE_DUMMY_SIGN_ENABLED=1
|
||||
|
||||
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}"
|
||||
tap_ok "Detached dummy signature added"
|
||||
|
||||
# Verify vith sign mechanism
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN}
|
||||
tap_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}
|
||||
tap_ok "commit with dummy signing"
|
||||
|
||||
if ${CMD_PREFIX} env -u OSTREE_DUMMY_SIGN_ENABLED ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} 2>err.txt; then
|
||||
fatal "verified dummy signature without env"
|
||||
fi
|
||||
# FIXME the error message here is broken
|
||||
#assert_file_has_content_literal err.txt 'dummy signature type is only for ostree testing'
|
||||
assert_file_has_content_literal err.txt ' No valid signatures found'
|
||||
tap_ok "dummy sig requires env"
|
||||
|
||||
tap_end
|
@ -21,56 +21,14 @@ set -euo pipefail
|
||||
|
||||
. $(dirname $0)/libtest.sh
|
||||
|
||||
echo "1..11"
|
||||
|
||||
# This is explicitly opt in for testing
|
||||
export OSTREE_DUMMY_SIGN_ENABLED=1
|
||||
|
||||
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
|
||||
# For multi-sign test
|
||||
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"
|
||||
|
||||
if ${CMD_PREFIX} env -u OSTREE_DUMMY_SIGN_ENABLED ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} 2>err.txt; then
|
||||
fatal "verified dummy signature without env"
|
||||
fi
|
||||
# FIXME the error message here is broken
|
||||
#assert_file_has_content_literal err.txt 'dummy signature type is only for ostree testing'
|
||||
assert_file_has_content_literal err.txt ' No valid signatures found'
|
||||
echo "ok dummy sig requires env"
|
||||
|
||||
# tests below require libsodium support
|
||||
if ! has_ostree_feature sign-ed25519; 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"
|
||||
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
|
||||
|
||||
# Test ostree sign with 'ed25519' module
|
||||
gen_ed25519_keys
|
||||
@ -87,7 +45,7 @@ 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"
|
||||
tap_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
|
||||
@ -99,9 +57,9 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed255
|
||||
${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"
|
||||
tap_ok "ed25519 signature verified"
|
||||
|
||||
# Check if we able to use all available modules to sign the same commit
|
||||
# Check if we are 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)"
|
||||
@ -121,7 +79,7 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed2551
|
||||
assert_file_has_content out.txt "ed25519: Signature verified successfully with key"
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} >out.txt
|
||||
assert_file_has_content out.txt "dummy: Signature verified"
|
||||
echo "ok multiple signing "
|
||||
tap_ok "multiple signing "
|
||||
|
||||
# Prepare files with public ed25519 signatures
|
||||
PUBKEYS="$(mktemp -p ${test_tmpdir} ed25519_XXXXXX.ed25519)"
|
||||
@ -163,7 +121,7 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed2551
|
||||
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"
|
||||
tap_ok "verify ed25519 keys file"
|
||||
|
||||
# Check ed25519 signing with secret file
|
||||
echo "Unsigned commit for secret file usage" >> file.txt
|
||||
@ -176,7 +134,7 @@ echo "${SECRET}" > ${KEYFILE}
|
||||
${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"
|
||||
tap_ok "sign with ed25519 keys file"
|
||||
|
||||
# Check the well-known places mechanism
|
||||
mkdir -p ${test_tmpdir}/{trusted,revoked}.ed25519.d
|
||||
@ -192,7 +150,7 @@ 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"
|
||||
tap_ok "verify ed25519 system-wide configuration"
|
||||
|
||||
# Add the public key into revoked list
|
||||
echo ${PUBLIC} > ${test_tmpdir}/revoked.ed25519.d/correct
|
||||
@ -201,4 +159,6 @@ if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=ed2
|
||||
exit 1
|
||||
fi
|
||||
rm -rf ${test_tmpdir}/{trusted,revoked}.ed25519.d
|
||||
echo "ok verify ed25519 revoking keys mechanism"
|
||||
tap_ok "verify ed25519 revoking keys mechanism"
|
||||
|
||||
tap_end
|
164
tests/test-signed-commit-spki.sh
Executable file
164
tests/test-signed-commit-spki.sh
Executable file
@ -0,0 +1,164 @@
|
||||
#!/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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
. $(dirname $0)/libtest.sh
|
||||
|
||||
# This is explicitly opt in for testing
|
||||
export OSTREE_DUMMY_SIGN_ENABLED=1
|
||||
|
||||
mkdir ${test_tmpdir}/repo
|
||||
ostree_repo_init repo --mode="archive"
|
||||
|
||||
# For multi-sign test
|
||||
DUMMYSIGN="dummysign"
|
||||
|
||||
# Test ostree sign with 'spki' module
|
||||
gen_spki_keys
|
||||
PUBLIC=${SPKIPUBLIC}
|
||||
SECRET=${SPKISECRET}
|
||||
|
||||
WRONG_PUBLIC="$(gen_spki_random_public)"
|
||||
|
||||
echo "PUBLIC = $PUBLIC"
|
||||
|
||||
echo "Signed commit with spki: ${SECRET}" >> file.txt
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo commit -b main -s "Signed with spki module" --sign="${SECRET}" --sign-type=spki
|
||||
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.spki &>/dev/null
|
||||
tap_ok "Detached spki signature added"
|
||||
|
||||
# Verify vith sign mechanism
|
||||
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${WRONG_PUBLIC}; then
|
||||
exit 1
|
||||
fi
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC}
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC} ${PUBLIC}
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} $(gen_spki_random_public) ${PUBLIC}
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} $(gen_spki_random_public) $(gen_spki_random_public) ${PUBLIC}
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC} $(gen_spki_random_public) $(gen_spki_random_public)
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} $(gen_spki_random_public) $(gen_spki_random_public) ${PUBLIC} $(gen_spki_random_public) $(gen_spki_random_public)
|
||||
tap_ok "spki signature verified"
|
||||
|
||||
# Check if we are 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" "spki"; 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=spki ${COMMIT} ${SECRET}
|
||||
# and verify
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki ${COMMIT} ${PUBLIC} >out.txt
|
||||
assert_file_has_content out.txt "spki: Signature verified successfully with key"
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=dummy --verify ${COMMIT} ${DUMMYSIGN} >out.txt
|
||||
assert_file_has_content out.txt "dummy: Signature verified"
|
||||
tap_ok "multiple signing "
|
||||
|
||||
# Prepare files with public spki signatures
|
||||
PUBKEYS="$(mktemp -p ${test_tmpdir} spki_XXXXXX.spki)"
|
||||
|
||||
# Test if file contain no keys
|
||||
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --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=spki --keys-file=${test_tmpdir} ${COMMIT}; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test with single key in list
|
||||
cat ${SPKIPUBLICPEM} > ${PUBKEYS}
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT} >out.txt
|
||||
assert_file_has_content out.txt 'spki: Signature verified successfully'
|
||||
|
||||
# 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
|
||||
gen_spki_random_public_pem
|
||||
done > ${PUBKEYS}
|
||||
# Check if file contain no valid signatures
|
||||
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT} 2>err.txt; then
|
||||
fatal "validated with no signatures"
|
||||
fi
|
||||
assert_file_has_content err.txt 'error:.* spki: Signature couldn.t be verified; tried 100 keys'
|
||||
# Check if no valid signatures provided via args&file
|
||||
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --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=spki --keys-file=${PUBKEYS} ${COMMIT} ${PUBLIC}
|
||||
|
||||
# Add correct key into the list
|
||||
cat "${SPKIPUBLICPEM}" >> ${PUBKEYS}
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT}
|
||||
|
||||
tap_ok "verify spki keys file"
|
||||
|
||||
# Check spki 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.spki)"
|
||||
cat "${SPKISECRETPEM}" > ${KEYFILE}
|
||||
# Sign
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --sign-type=spki --keys-file=${KEYFILE} ${COMMIT}
|
||||
# Verify
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-file=${PUBKEYS} ${COMMIT}
|
||||
tap_ok "sign with spki keys file"
|
||||
|
||||
# Check the well-known places mechanism
|
||||
mkdir -p ${test_tmpdir}/{trusted,revoked}.spki.d
|
||||
for((i=0;i<100;i++)); do
|
||||
# Generate some key files with random public signatures
|
||||
gen_spki_random_public_pem > ${test_tmpdir}/trusted.spki.d/signature_$i
|
||||
done
|
||||
# Check no valid public keys are available
|
||||
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-dir=${test_tmpdir} ${COMMIT}; then
|
||||
exit 1
|
||||
fi
|
||||
cat "${SPKIPUBLICPEM}" > ${test_tmpdir}/trusted.spki.d/correct
|
||||
# Verify with correct key
|
||||
${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-dir=${test_tmpdir} ${COMMIT}
|
||||
|
||||
tap_ok "verify spki system-wide configuration"
|
||||
|
||||
# Add the public key into revoked list
|
||||
cat "${SPKIPUBLICPEM}" > ${test_tmpdir}/revoked.spki.d/correct
|
||||
# Check if public key is not valid anymore
|
||||
if ${CMD_PREFIX} ostree --repo=${test_tmpdir}/repo sign --verify --sign-type=spki --keys-dir=${test_tmpdir} ${COMMIT}; then
|
||||
exit 1
|
||||
fi
|
||||
rm -rf ${test_tmpdir}/{trusted,revoked}.spki.d
|
||||
tap_ok "verify spki revoking keys mechanism"
|
||||
|
||||
tap_end
|
Loading…
Reference in New Issue
Block a user