diff --git a/README b/README
index a198637d13..9a551f76fa 100644
--- a/README
+++ b/README
@@ -230,7 +230,6 @@ REQUIREMENTS:
libarchive >= 3.0 (optional)
libxkbcommon >= 0.3.0 (optional)
libpcre2 (optional)
- libgcrypt (optional)
libqrencode >= 3 (optional)
libmicrohttpd >= 0.9.33 (optional)
libcurl >= 7.32.0 (optional)
diff --git a/man/sd-journal.xml b/man/sd-journal.xml
index da4e4e1f82..bcde6779b5 100644
--- a/man/sd-journal.xml
+++ b/man/sd-journal.xml
@@ -94,7 +94,7 @@
sd_journal objects might cause optional shared libraries to be dynamically
loaded via
dlopen3,
- such as decompression libraries (xz, lz4, zstd) or cryptographic libraries (gcrypt).
+ such as decompression libraries (xz, lz4, zstd).
diff --git a/meson.build b/meson.build
index f953eeab13..93df01181b 100644
--- a/meson.build
+++ b/meson.build
@@ -1206,8 +1206,9 @@ libqrencode = dependency('libqrencode',
libqrencode_cflags = libqrencode.partial_dependency(includes: true, compile_args: true)
conf.set10('HAVE_QRENCODE', libqrencode.found())
+feature = get_option('gcrypt')
libgcrypt = dependency('libgcrypt',
- required : get_option('gcrypt'))
+ required : feature.disabled() ? feature : false)
conf.set10('HAVE_GCRYPT', libgcrypt.found())
if libgcrypt.found()
libgcrypt_cflags = libgcrypt.partial_dependency(includes: true, compile_args: true)
@@ -1813,8 +1814,7 @@ if static_libsystemd != 'false'
install_tag: 'libsystemd',
install_dir : libdir,
pic : static_libsystemd_pic,
- dependencies : [libgcrypt_cflags,
- liblz4_cflags,
+ dependencies : [liblz4_cflags,
libm,
libucontext,
libxz_cflags,
diff --git a/src/basic/build.c b/src/basic/build.c
index 213ccca5f4..965d4f5a36 100644
--- a/src/basic/build.c
+++ b/src/basic/build.c
@@ -64,12 +64,6 @@ const char* const systemd_features =
/* cryptographic libraries */
-#if HAVE_GCRYPT
- " +GCRYPT"
-#else
- " -GCRYPT"
-#endif
-
#if HAVE_GNUTLS
" +GNUTLS"
#else
diff --git a/src/basic/meson.build b/src/basic/meson.build
index 90a464f2c4..31fd129871 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -42,7 +42,6 @@ basic_sources = files(
'format-ifname.c',
'format-util.c',
'fs-util.c',
- 'gcrypt-util.c',
'glob-util.c',
'glyph-util.c',
'gunicode.c',
@@ -214,7 +213,6 @@ libbasic_static = static_library(
include_directories : basic_includes,
implicit_include_directories : false,
dependencies : [libbzip2_cflags,
- libgcrypt_cflags,
liblz4_cflags,
libxz_cflags,
libz_cflags,
diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c
index 95d28ba1a5..44b169c591 100644
--- a/src/journal-remote/journal-remote-main.c
+++ b/src/journal-remote/journal-remote-main.c
@@ -15,6 +15,7 @@
#include "format-util.h"
#include "fileio.h"
#include "hashmap.h"
+#include "journal-authenticate.h"
#include "journal-compression-util.h"
#include "journal-remote.h"
#include "journal-remote-write.h"
@@ -1166,6 +1167,8 @@ static int run(int argc, char **argv) {
log_setup();
+ journal_auth_init();
+
r = parse_config();
if (r < 0)
return r;
diff --git a/src/journal/journalctl-authenticate.c b/src/journal/journalctl-authenticate.c
index 721404a1f5..73c1579b88 100644
--- a/src/journal/journalctl-authenticate.c
+++ b/src/journal/journalctl-authenticate.c
@@ -7,11 +7,11 @@
#include "alloc-util.h"
#include "ansi-color.h"
#include "chattr-util.h"
+#include "crypto-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fs-util.h"
-#include "fsprg.h"
-#include "gcrypt-util.h"
+#include "fsprg-openssl.h"
#include "hostname-setup.h"
#include "hostname-util.h"
#include "io-util.h"
@@ -30,7 +30,7 @@
#include "time-util.h"
#include "tmpfile-util.h"
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
static int format_key(
const struct iovec *seed,
uint64_t start,
@@ -60,7 +60,7 @@ static int format_key(
#endif
int action_setup_keys(void) {
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
_cleanup_(unlink_and_freep) char *tmpfile = NULL;
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *path = NULL;
@@ -70,7 +70,7 @@ int action_setup_keys(void) {
assert(arg_action == ACTION_SETUP_KEYS);
- r = DLOPEN_GCRYPT(LOG_ERR, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED);
+ r = DLOPEN_LIBCRYPTO(LOG_ERR, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED);
if (r < 0)
return r;
@@ -103,9 +103,8 @@ int action_setup_keys(void) {
"Sealing key file %s exists already. Use --force to recreate.", path);
_cleanup_(iovec_erase) struct iovec
- mpk = IOVEC_ALLOCA(FSPRG_mpkinbytes(FSPRG_RECOMMENDED_SECPAR)),
seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN),
- state = IOVEC_ALLOCA(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR));
+ state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR));
if (!arg_quiet)
log_info("Generating seed...");
@@ -113,15 +112,9 @@ int action_setup_keys(void) {
if (r < 0)
return log_error_errno(r, "Failed to acquire random seed: %m");
- if (!arg_quiet)
- log_info("Generating key pair...");
- r = FSPRG_GenMK(NULL, mpk.iov_base, seed.iov_base, seed.iov_len, FSPRG_RECOMMENDED_SECPAR);
- if (r < 0)
- return log_error_errno(r, "Failed to generate key pair: %m");
-
if (!arg_quiet)
log_info("Generating sealing key...");
- r = FSPRG_GenState0(state.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len);
+ r = fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, /* epoch= */ 0, &seed, &state);
if (r < 0)
return log_error_errno(r, "Failed to generate sealing key: %m");
diff --git a/src/journal/journalctl-misc.c b/src/journal/journalctl-misc.c
index be0b09d8fe..000c1c6a2b 100644
--- a/src/journal/journalctl-misc.c
+++ b/src/journal/journalctl-misc.c
@@ -9,6 +9,7 @@
#include "format-table.h"
#include "format-util.h"
#include "hashmap.h"
+#include "journal-authenticate.h"
#include "journal-internal.h"
#include "journal-verify.h"
#include "journalctl.h"
@@ -40,6 +41,8 @@ int action_verify(void) {
assert(arg_action == ACTION_VERIFY);
+ journal_auth_init();
+
r = acquire_journal(&j);
if (r < 0)
return r;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index b5c4ec7aee..3ee4da9013 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -81,7 +81,7 @@ bool arg_file_stdin = false;
int arg_priorities = 0;
Set *arg_facilities = NULL;
char *arg_verify_key = NULL;
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
bool arg_force = false;
#endif
@@ -769,7 +769,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
OPTION_GROUP("Forward Secure Sealing (FSS) Options"): {}
OPTION_LONG("interval", "TIME", "Time interval for changing the FSS sealing key"):
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
r = parse_sec(opts.arg, &arg_interval);
if (r < 0 || arg_interval <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -781,7 +781,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
#endif
OPTION_LONG("verify-key", "KEY", "Specify FSS verification key"):
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
erase_and_free(arg_verify_key);
arg_verify_key = strdup(opts.arg);
if (!arg_verify_key)
@@ -800,7 +800,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
#endif
OPTION_LONG("force", NULL, "Override of the FSS key pair with --setup-keys"):
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
arg_force = true;
break;
#else
@@ -909,7 +909,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
break;
OPTION_LONG("setup-keys", NULL, "Generate a new FSS key pair"):
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
arg_action = ACTION_SETUP_KEYS;
break;
#else
diff --git a/src/journal/journalctl.h b/src/journal/journalctl.h
index 406a16077b..504e5e6dd1 100644
--- a/src/journal/journalctl.h
+++ b/src/journal/journalctl.h
@@ -57,7 +57,7 @@ extern bool arg_file_stdin;
extern int arg_priorities;
extern Set *arg_facilities;
extern char *arg_verify_key;
-#if HAVE_GCRYPT
+#if HAVE_OPENSSL
extern usec_t arg_interval;
extern bool arg_force;
#endif
diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c
index 764e5091ab..68f95ea967 100644
--- a/src/journal/journald-manager.c
+++ b/src/journal/journald-manager.c
@@ -697,7 +697,10 @@ static int manager_archive_offline_user_journals(Manager *m) {
TAKE_FD(fd); /* Donated to journal_file_open() */
- journal_file_write_final_tag(f);
+ r = journal_file_auth_append_tag(f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to append tag when closing journal, ignoring: %m");
+
r = journal_file_archive(f, NULL);
if (r < 0)
log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full);
@@ -2539,18 +2542,16 @@ int manager_init(Manager *m) {
}
void manager_maybe_append_tags(Manager *m) {
-#if HAVE_GCRYPT
- JournalFile *f;
- usec_t n;
+ assert(m);
- n = now(CLOCK_REALTIME);
+ usec_t n = now(CLOCK_REALTIME);
if (m->system_journal)
- journal_file_maybe_append_tag(m->system_journal, n);
+ journal_file_auth_append_tag_maybe(m->system_journal, n);
+ JournalFile *f;
ORDERED_HASHMAP_FOREACH(f, m->user_journals)
- journal_file_maybe_append_tag(f, n);
-#endif
+ journal_file_auth_append_tag_maybe(f, n);
}
Manager* manager_free(Manager *m) {
diff --git a/src/journal/journald.c b/src/journal/journald.c
index a06752d32e..91f02178f5 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -52,6 +52,8 @@ static int run(int argc, char *argv[]) {
sigbus_install();
+ journal_auth_init();
+
r = manager_new(&m);
if (r < 0)
return log_oom();
@@ -110,14 +112,12 @@ static int run(int argc, char *argv[]) {
} else
t = USEC_INFINITY;
-#if HAVE_GCRYPT
if (m->system_journal) {
usec_t u;
- if (journal_file_next_evolve_usec(m->system_journal, &u))
+ if (journal_file_auth_next_evolve_usec(m->system_journal, &u) >= 0)
t = MIN(t, usec_sub_unsigned(u, n));
}
-#endif
r = sd_event_run(m->event, t);
if (r < 0)
diff --git a/src/journal/meson.build b/src/journal/meson.build
index 38f2e7bb7e..e39381f03f 100644
--- a/src/journal/meson.build
+++ b/src/journal/meson.build
@@ -94,6 +94,7 @@ executables += [
'link_with' : journalctl_link_with,
'dependencies' : [
liblz4_cflags,
+ libopenssl_cflags,
libxz_cflags,
libzstd_cflags,
],
diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
index 24979e8882..d1797722a4 100644
--- a/src/libsystemd/meson.build
+++ b/src/libsystemd/meson.build
@@ -3,8 +3,7 @@
sd_journal_sources = files(
'sd-journal/audit-type.c',
'sd-journal/catalog.c',
- 'sd-journal/fsprg.c',
- 'sd-journal/journal-authenticate.c',
+ 'sd-journal/journal-authenticate-internal.c',
'sd-journal/journal-file.c',
'sd-journal/journal-send.c',
'sd-journal/journal-vacuum.c',
diff --git a/src/libsystemd/sd-journal/journal-authenticate-internal.c b/src/libsystemd/sd-journal/journal-authenticate-internal.c
new file mode 100644
index 0000000000..acb4c221bc
--- /dev/null
+++ b/src/libsystemd/sd-journal/journal-authenticate-internal.c
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "journal-authenticate-internal.h"
+#include "journal-file.h"
+
+static const JournalAuthOps *auth_ops = NULL;
+
+void journal_auth_set_ops(const JournalAuthOps *ops) {
+ assert(ops);
+ assert(!auth_ops || auth_ops == ops);
+
+ auth_ops = ops;
+}
+
+bool journal_auth_supported(void) {
+ return !!auth_ops;
+}
+
+void journal_file_auth_done(JournalFile *f) {
+ assert(f);
+
+ if (!auth_ops)
+ return;
+
+ assert(auth_ops->free);
+ f->auth_context = auth_ops->free(f->auth_context);
+}
+
+int journal_file_auth_load(JournalFile *f) {
+ assert(f);
+
+ if (!auth_ops)
+ return -EOPNOTSUPP;
+
+ if (f->auth_context)
+ return -EBUSY;
+
+ assert(auth_ops->load);
+ return auth_ops->load(&f->auth_context);
+}
+
+int journal_file_auth_load_key(JournalFile *f, const char *key) {
+ assert(f);
+
+ if (!auth_ops)
+ return -EOPNOTSUPP;
+
+ if (f->auth_context)
+ return -EBUSY;
+
+ assert(auth_ops->load_key);
+ return auth_ops->load_key(&f->auth_context, key);
+}
+
+int journal_file_auth_epoch_to_realtime_usec(JournalFile *f, uint64_t epoch, usec_t *ret_start, usec_t *ret_end) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return -EOPNOTSUPP;
+
+ assert(auth_ops);
+ assert(auth_ops->epoch_to_realtime_usec);
+ return auth_ops->epoch_to_realtime_usec(f->auth_context, epoch, ret_start, ret_end);
+}
+
+int journal_file_auth_next_evolve_usec(JournalFile *f, usec_t *ret) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return -EOPNOTSUPP;
+
+ assert(auth_ops);
+ assert(auth_ops->next_evolve_usec);
+ return auth_ops->next_evolve_usec(f->auth_context, ret);
+}
+
+int journal_file_auth_seek(JournalFile *f, uint64_t goal) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return 0;
+
+ assert(auth_ops);
+ assert(auth_ops->seek);
+ return auth_ops->seek(f->auth_context, goal);
+}
+
+int journal_file_auth_start(JournalFile *f) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return 0;
+
+ assert(auth_ops);
+ assert(auth_ops->start);
+ return auth_ops->start(f->auth_context);
+}
+
+int journal_file_auth_end(JournalFile *f, uint8_t ret[static TAG_LENGTH]) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return -EOPNOTSUPP;
+
+ assert(auth_ops);
+ assert(auth_ops->end);
+ return auth_ops->end(f->auth_context, ret);
+}
+
+int journal_file_auth_put_header(JournalFile *f) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return 0;
+
+ assert(auth_ops);
+ assert(auth_ops->put_header);
+ return auth_ops->put_header(f->auth_context, f);
+}
+
+int journal_file_auth_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return 0;
+
+ assert(auth_ops);
+ assert(auth_ops->put_object);
+ return auth_ops->put_object(f->auth_context, f, type, o, p);
+}
+
+int journal_file_auth_append_tag(JournalFile *f) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return 0;
+
+ if (!journal_file_writable(f))
+ return 0;
+
+ assert(auth_ops);
+ assert(auth_ops->append_tag);
+ return auth_ops->append_tag(f->auth_context, f);
+}
+
+int journal_file_auth_append_tag_first(JournalFile *f) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return 0;
+
+ if (!journal_file_writable(f))
+ return 0;
+
+ assert(auth_ops);
+ assert(auth_ops->append_tag_first);
+ return auth_ops->append_tag_first(f->auth_context, f);
+}
+
+int journal_file_auth_append_tag_maybe(JournalFile *f, usec_t realtime) {
+ assert(f);
+
+ if (!JOURNAL_HEADER_SEALED(f->header))
+ return 0;
+
+ if (!journal_file_writable(f))
+ return 0;
+
+ assert(auth_ops);
+ assert(auth_ops->append_tag_maybe);
+ return auth_ops->append_tag_maybe(f->auth_context, f, realtime);
+}
diff --git a/src/libsystemd/sd-journal/journal-authenticate-internal.h b/src/libsystemd/sd-journal/journal-authenticate-internal.h
new file mode 100644
index 0000000000..5d5d94be20
--- /dev/null
+++ b/src/libsystemd/sd-journal/journal-authenticate-internal.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "journal-def.h"
+#include "sd-forward.h"
+
+typedef struct JournalAuthOps {
+ JournalAuthContext* (*free)(JournalAuthContext *c);
+ int (*load)(JournalAuthContext **ret);
+ int (*load_key)(JournalAuthContext **ret, const char *key);
+ int (*epoch_to_realtime_usec)(const JournalAuthContext *c, uint64_t epoch, usec_t *ret_start, usec_t *ret_end);
+ int (*next_evolve_usec)(const JournalAuthContext *c, usec_t *ret);
+ int (*seek)(JournalAuthContext *c, uint64_t goal);
+ int (*start)(JournalAuthContext *c);
+ int (*end)(JournalAuthContext *c, uint8_t ret[static TAG_LENGTH]);
+ int (*put_header)(JournalAuthContext *c, JournalFile *f);
+ int (*put_object)(JournalAuthContext *c, JournalFile *f, ObjectType type, Object *o, uint64_t p);
+ int (*append_tag)(JournalAuthContext *c, JournalFile *f);
+ int (*append_tag_first)(JournalAuthContext *c, JournalFile *f);
+ int (*append_tag_maybe)(JournalAuthContext *c, JournalFile *f, usec_t realtime);
+} JournalAuthOps;
+
+void journal_auth_set_ops(const JournalAuthOps *ops);
+bool journal_auth_supported(void);
+
+void journal_file_auth_done(JournalFile *f);
+int journal_file_auth_load(JournalFile *f);
+int journal_file_auth_load_key(JournalFile *f, const char *key);
+int journal_file_auth_epoch_to_realtime_usec(JournalFile *f, uint64_t epoch, usec_t *ret_start, usec_t *ret_end);
+int journal_file_auth_next_evolve_usec(JournalFile *f, usec_t *ret);
+int journal_file_auth_seek(JournalFile *f, uint64_t goal);
+int journal_file_auth_start(JournalFile *f);
+int journal_file_auth_end(JournalFile *f, uint8_t ret[static TAG_LENGTH]);
+int journal_file_auth_put_header(JournalFile *f);
+int journal_file_auth_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p);
+int journal_file_auth_append_tag(JournalFile *f);
+int journal_file_auth_append_tag_first(JournalFile *f);
+int journal_file_auth_append_tag_maybe(JournalFile *f, usec_t realtime);
diff --git a/src/libsystemd/sd-journal/journal-authenticate.c b/src/libsystemd/sd-journal/journal-authenticate.c
deleted file mode 100644
index 86be008aa1..0000000000
--- a/src/libsystemd/sd-journal/journal-authenticate.c
+++ /dev/null
@@ -1,555 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include
-#include
-#include
-
-#include "alloc-util.h"
-#include "fd-util.h"
-#include "fsprg.h"
-#include "gcrypt-util.h"
-#include "hexdecoct.h"
-#include "iovec-util.h"
-#include "journal-authenticate.h"
-#include "journal-def.h"
-#include "journal-file.h"
-#include "log.h"
-#include "memory-util.h"
-#include "string-util.h"
-#include "time-util.h"
-
-static void* fssheader_free(FSSHeader *p) {
- /* mmap() returns MAP_FAILED on error and sets the errno */
- if (!p || p == MAP_FAILED)
- return NULL;
-
- assert_se(munmap(p, PAGE_ALIGN(sizeof(FSSHeader))) >= 0);
- return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader*, fssheader_free);
-
-int journal_file_append_tag(JournalFile *f) {
-#if HAVE_GCRYPT
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- if (!f->hmac_running) {
- r = journal_file_hmac_start(f);
- if (r < 0)
- return r;
- }
-
- assert(f->hmac);
-
- Object *o;
- uint64_t p;
- r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
- if (r < 0)
- return r;
-
- uint64_t seqnum = le64toh(f->header->n_tags) + 1;
- f->header->n_tags = htole64(seqnum);
-
- o->tag.seqnum = htole64(seqnum);
- o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state.iov_base));
-
- log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
- le64toh(o->tag.seqnum), le64toh(o->tag.epoch));
-
- /* Add the tag object itself, so that we can protect its
- * header. This will exclude the actual hash value in it */
- r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
- if (r < 0)
- return r;
-
- /* Get the HMAC tag and store it in the object */
- memcpy(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH);
- f->hmac_running = false;
-
- return 0;
-#else
- return -EOPNOTSUPP;
-#endif
-}
-
-int journal_file_hmac_start(JournalFile *f) {
-#if HAVE_GCRYPT
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- if (f->hmac_running)
- return 0;
-
- /* Prepare HMAC for next cycle */
- sym_gcry_md_reset(f->hmac);
-
- uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
- CLEANUP_ERASE(key);
- r = FSPRG_GetKey(f->fsprg_state.iov_base, key, sizeof(key), 0);
- if (r < 0)
- return r;
-
- gcry_error_t err = sym_gcry_md_setkey(f->hmac, key, sizeof(key));
- if (gcry_err_code(err) != GPG_ERR_NO_ERROR)
- return log_debug_errno(SYNTHETIC_ERRNO(EIO),
- "sym_gcry_md_setkey() failed with error code: %s",
- sym_gcry_strerror(err));
-
- f->hmac_running = true;
-
- return 0;
-#else
- return -EOPNOTSUPP;
-#endif
-}
-
-static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
- uint64_t t;
-
- assert(f);
- assert(epoch);
- assert(JOURNAL_HEADER_SEALED(f->header));
-
- if (f->fss_start_usec == 0 || f->fss_interval_usec == 0)
- return -EOPNOTSUPP;
-
- if (realtime < f->fss_start_usec)
- return -ESTALE;
-
- t = realtime - f->fss_start_usec;
- t = t / f->fss_interval_usec;
-
- *epoch = t;
-
- return 0;
-}
-
-static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
- uint64_t goal, epoch;
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- r = journal_file_get_epoch(f, realtime, &goal);
- if (r < 0)
- return r;
-
- epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
- if (epoch > goal)
- return -ESTALE;
-
- return epoch != goal;
-}
-
-int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
- uint64_t goal, epoch;
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- r = journal_file_get_epoch(f, realtime, &goal);
- if (r < 0)
- return r;
-
- epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
- if (epoch < goal)
- log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
-
- for (;;) {
- if (epoch > goal)
- return -ESTALE;
- if (epoch == goal)
- return 0;
-
- r = FSPRG_Evolve(f->fsprg_state.iov_base);
- if (r < 0)
- return r;
-
- epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
- if (epoch < goal) {
- r = journal_file_append_tag(f);
- if (r < 0)
- return r;
- }
- }
-}
-
-int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- assert(iovec_is_set(&f->fsprg_seed));
-
- if (iovec_is_set(&f->fsprg_state)) {
- /* Cheaper... */
-
- uint64_t epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
- if (goal == epoch)
- return 0;
-
- if (goal == epoch + 1)
- return FSPRG_Evolve(f->fsprg_state.iov_base);
- } else {
- r = iovec_alloc(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR), &f->fsprg_state);
- if (r < 0)
- return r;
- }
-
- log_debug("Seeking FSPRG key to %"PRIu64".", goal);
-
- _cleanup_(iovec_erase) struct iovec msk = IOVEC_ALLOCA(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
- r = FSPRG_GenMK(msk.iov_base, NULL, f->fsprg_seed.iov_base, f->fsprg_seed.iov_len, FSPRG_RECOMMENDED_SECPAR);
- if (r < 0)
- return r;
-
- return FSPRG_Seek(f->fsprg_state.iov_base, goal, msk.iov_base, f->fsprg_seed.iov_base, f->fsprg_seed.iov_len);
-}
-
-int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- if (realtime <= 0)
- realtime = now(CLOCK_REALTIME);
-
- r = journal_file_fsprg_need_evolve(f, realtime);
- if (r <= 0)
- return 0;
-
- r = journal_file_append_tag(f);
- if (r < 0)
- return r;
-
- r = journal_file_fsprg_evolve(f, realtime);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
-#if HAVE_GCRYPT
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- r = journal_file_hmac_start(f);
- if (r < 0)
- return r;
-
- if (!o) {
- r = journal_file_move_to_object(f, type, p, &o);
- if (r < 0)
- return r;
- } else if (type > OBJECT_UNUSED && o->object.type != type)
- return -EBADMSG;
-
- sym_gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
-
- switch (o->object.type) {
-
- case OBJECT_DATA:
- /* All but hash and payload are mutable */
- sym_gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
- sym_gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
- break;
-
- case OBJECT_FIELD:
- /* Same here */
- sym_gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
- sym_gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
- break;
-
- case OBJECT_ENTRY:
- /* All */
- sym_gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
- break;
-
- case OBJECT_FIELD_HASH_TABLE:
- case OBJECT_DATA_HASH_TABLE:
- case OBJECT_ENTRY_ARRAY:
- /* Nothing: everything is mutable */
- break;
-
- case OBJECT_TAG:
- /* All but the tag itself */
- sym_gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
- sym_gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-#else
- return -EOPNOTSUPP;
-#endif
-}
-
-int journal_file_hmac_put_header(JournalFile *f) {
-#if HAVE_GCRYPT
- int r;
-
- assert(f);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- r = journal_file_hmac_start(f);
- if (r < 0)
- return r;
-
- /* All but state+reserved, boot_id, arena_size,
- * tail_object_offset, n_objects, n_entries,
- * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
- * head_entry_realtime, tail_entry_realtime,
- * tail_entry_monotonic, n_data, n_fields, n_tags,
- * n_entry_arrays. */
-
- sym_gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
- sym_gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
- sym_gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
- sym_gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
-
- return 0;
-#else
- return -EOPNOTSUPP;
-#endif
-}
-
-int journal_file_fss_load(JournalFile *f) {
- _cleanup_close_ int fd = -EBADF;
- _cleanup_free_ char *path = NULL;
- _cleanup_(fssheader_freep) FSSHeader *header = NULL;
- struct stat st;
- sd_id128_t machine;
- int r;
-
- assert(f);
-
- /* This function is used to determine whether sealing should be enabled in the journal header so we
- * can't check the header to check if sealing is enabled here. */
-
- r = sd_id128_get_machine(&machine);
- if (r < 0)
- return r;
-
- if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
- SD_ID128_FORMAT_VAL(machine)) < 0)
- return -ENOMEM;
-
- fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
- if (fd < 0) {
- if (errno != ENOENT)
- log_error_errno(errno, "Failed to open %s: %m", path);
-
- return -errno;
- }
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (st.st_size < (off_t) sizeof(FSSHeader))
- return -ENODATA;
-
- header = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
- if (header == MAP_FAILED)
- return -errno;
-
- if (memcmp(header->signature, (const uint8_t[]) FSS_HEADER_SIGNATURE, 8) != 0)
- return -EBADMSG;
-
- if (header->incompatible_flags != 0)
- return -EPROTONOSUPPORT;
-
- if (le64toh(header->header_size) < sizeof(FSSHeader))
- return -EBADMSG;
-
- if (le64toh(header->fsprg_state_size) != FSPRG_stateinbytes(le16toh(header->fsprg_secpar)))
- return -EBADMSG;
-
- f->fss_file_size = le64toh(header->header_size) + le64toh(header->fsprg_state_size);
- if ((uint64_t) st.st_size < f->fss_file_size)
- return -ENODATA;
-
- if (!sd_id128_equal(machine, header->machine_id))
- return -EHOSTDOWN;
-
- if (le64toh(header->start_usec) <= 0 || le64toh(header->interval_usec) <= 0)
- return -EBADMSG;
-
- size_t sz = PAGE_ALIGN(f->fss_file_size);
- assert(sz < SIZE_MAX);
- f->fss_file = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
- if (f->fss_file == MAP_FAILED) {
- f->fss_file = NULL;
- return -errno;
- }
-
- f->fss_start_usec = le64toh(f->fss_file->start_usec);
- f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
-
- f->fsprg_state = IOVEC_MAKE(
- (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size),
- le64toh(f->fss_file->fsprg_state_size));
-
- return 0;
-}
-
-int journal_file_hmac_setup(JournalFile *f) {
-#if HAVE_GCRYPT
- gcry_error_t e;
- int r;
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- r = initialize_libgcrypt(true);
- if (r < 0)
- return r;
-
- e = sym_gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
- if (e != 0)
- return -EOPNOTSUPP;
-
- return 0;
-#else
- return -EOPNOTSUPP;
-#endif
-}
-
-int journal_file_append_first_tag(JournalFile *f) {
- uint64_t p;
- int r;
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return 0;
-
- log_debug("Calculating first tag...");
-
- r = journal_file_hmac_put_header(f);
- if (r < 0)
- return r;
-
- p = le64toh(f->header->field_hash_table_offset);
- if (p < offsetof(Object, hash_table.items))
- return -EINVAL;
- p -= offsetof(Object, hash_table.items);
-
- r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
- if (r < 0)
- return r;
-
- p = le64toh(f->header->data_hash_table_offset);
- if (p < offsetof(Object, hash_table.items))
- return -EINVAL;
- p -= offsetof(Object, hash_table.items);
-
- r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
- if (r < 0)
- return r;
-
- r = journal_file_append_tag(f);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int journal_file_parse_verification_key(JournalFile *f, const char *key) {
- _cleanup_(erase_and_freep) uint8_t *seed = NULL;
- size_t seed_size;
- const char *k;
- int r;
-
- assert(f);
- assert(key);
-
- seed_size = FSPRG_RECOMMENDED_SEEDLEN;
- seed = malloc(seed_size);
- if (!seed)
- return -ENOMEM;
-
- k = key;
- for (size_t c = 0; c < seed_size; c++) {
- int x, y;
-
- k = skip_leading_chars(k, "-");
-
- x = unhexchar(*k);
- if (x < 0)
- return -EKEYREJECTED;
- k++;
-
- y = unhexchar(*k);
- if (y < 0)
- return -EKEYREJECTED;
- k++;
-
- seed[c] = (uint8_t) (x * 16 + y);
- }
-
- if (*k != '/')
- return -EKEYREJECTED;
- k++;
-
- uint64_t start, interval;
- r = sscanf(k, "%"PRIx64"-%"PRIx64, &start, &interval);
- if (r != 2)
- return -EKEYREJECTED;
-
- if (start == 0 || interval == 0)
- return -EKEYREJECTED;
-
- uint64_t start_usec;
- if (!MUL_SAFE(&start_usec, start, interval))
- return -EKEYREJECTED;
-
- f->fsprg_seed = IOVEC_MAKE(TAKE_PTR(seed), seed_size);
- f->fss_start_usec = start_usec;
- f->fss_interval_usec = interval;
-
- return 0;
-}
-
-bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
- uint64_t epoch;
-
- assert(f);
- assert(u);
-
- if (!JOURNAL_HEADER_SEALED(f->header))
- return false;
-
- epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
-
- *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
-
- return true;
-}
diff --git a/src/libsystemd/sd-journal/journal-authenticate.h b/src/libsystemd/sd-journal/journal-authenticate.h
deleted file mode 100644
index 89897fc0a7..0000000000
--- a/src/libsystemd/sd-journal/journal-authenticate.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-forward.h"
-#include "journal-file.h"
-
-int journal_file_append_tag(JournalFile *f);
-int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime);
-int journal_file_append_first_tag(JournalFile *f);
-
-int journal_file_hmac_setup(JournalFile *f);
-int journal_file_hmac_start(JournalFile *f);
-int journal_file_hmac_put_header(JournalFile *f);
-int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p);
-
-int journal_file_fss_load(JournalFile *f);
-int journal_file_parse_verification_key(JournalFile *f, const char *key);
-
-int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime);
-int journal_file_fsprg_seek(JournalFile *f, uint64_t goal);
-
-bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u);
diff --git a/src/libsystemd/sd-journal/journal-def.h b/src/libsystemd/sd-journal/journal-def.h
index 026aa4c941..a48554cee1 100644
--- a/src/libsystemd/sd-journal/journal-def.h
+++ b/src/libsystemd/sd-journal/journal-def.h
@@ -43,6 +43,9 @@ typedef struct HashItem HashItem;
typedef struct FSSHeader FSSHeader;
+typedef struct JournalFile JournalFile;
+typedef struct JournalAuthContext JournalAuthContext;
+
/* Object types */
typedef enum ObjectType {
OBJECT_UNUSED, /* also serves as "any type" or "additional category" */
@@ -207,7 +210,8 @@ enum {
HEADER_COMPATIBLE_TAIL_ENTRY_BOOT_ID |
HEADER_COMPATIBLE_SEALED_CONTINUOUS,
- HEADER_COMPATIBLE_SUPPORTED = (HAVE_GCRYPT ? HEADER_COMPATIBLE_SEALED | HEADER_COMPATIBLE_SEALED_CONTINUOUS : 0) |
+ HEADER_COMPATIBLE_SUPPORTED = HEADER_COMPATIBLE_SEALED |
+ HEADER_COMPATIBLE_SEALED_CONTINUOUS |
HEADER_COMPATIBLE_TAIL_ENTRY_BOOT_ID,
};
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
index 37392ea9bb..41e06086d2 100644
--- a/src/libsystemd/sd-journal/journal-file.c
+++ b/src/libsystemd/sd-journal/journal-file.c
@@ -19,11 +19,9 @@
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
-#include "gcrypt-util.h"
#include "hashmap.h"
#include "id128-util.h"
-#include "iovec-util.h"
-#include "journal-authenticate.h"
+#include "journal-authenticate-internal.h"
#include "journal-def.h"
#include "journal-file.h"
#include "journal-internal.h"
@@ -308,17 +306,7 @@ JournalFile* journal_file_close(JournalFile *f) {
free(f->compress_buffer);
#endif
- if (f->fss_file) {
- size_t sz = PAGE_ALIGN(f->fss_file_size);
- assert(sz < SIZE_MAX);
- munmap(f->fss_file, sz);
- } else
- iovec_done_erase(&f->fsprg_state);
-
- iovec_done_erase(&f->fsprg_seed);
-
- if (f->hmac)
- sym_gcry_md_close(f->hmac);
+ journal_file_auth_done(f);
return mfree(f);
}
@@ -404,16 +392,13 @@ static int journal_file_init_header(
JournalFileFlags file_flags,
JournalFile *template) {
- bool seal = false;
ssize_t k;
int r;
assert(f);
-#if HAVE_GCRYPT
/* Try to load the FSPRG state, and if we can't, then just don't do sealing */
- seal = FLAGS_SET(file_flags, JOURNAL_SEAL) && journal_file_fss_load(f) >= 0;
-#endif
+ bool seal = FLAGS_SET(file_flags, JOURNAL_SEAL) && journal_file_auth_load(f) >= 0;
Header h = {
.header_size = htole64(ALIGN64(sizeof(h))),
@@ -472,14 +457,19 @@ static int journal_file_refresh_header(JournalFile *f) {
}
static bool warn_wrong_flags(const JournalFile *f, bool compatible) {
- const uint32_t any = compatible ? HEADER_COMPATIBLE_ANY : HEADER_INCOMPATIBLE_ANY,
- supported = compatible ? HEADER_COMPATIBLE_SUPPORTED : HEADER_INCOMPATIBLE_SUPPORTED;
+ const uint32_t any = compatible ? HEADER_COMPATIBLE_ANY : HEADER_INCOMPATIBLE_ANY;
+ uint32_t supported = compatible ? HEADER_COMPATIBLE_SUPPORTED : HEADER_INCOMPATIBLE_SUPPORTED;
const char *type = compatible ? "compatible" : "incompatible";
uint32_t flags;
assert(f);
assert(f->header);
+ /* When sealing is not supported, refuse to write to an already sealed journal file, but still allow
+ * reading sealed journal files. */
+ if (compatible && journal_file_writable(f) && !journal_auth_supported())
+ supported &= ~(HEADER_COMPATIBLE_SEALED | HEADER_COMPATIBLE_SEALED_CONTINUOUS);
+
flags = le32toh(compatible ? f->header->compatible_flags : f->header->incompatible_flags);
if (flags & ~supported) {
@@ -1833,11 +1823,9 @@ static int journal_file_append_field(
/* The linking might have altered the window, so let's only pass the offset to hmac which will
* move to the object again if needed. */
-#if HAVE_GCRYPT
- r = journal_file_hmac_put_object(f, OBJECT_FIELD, NULL, p);
+ r = journal_file_auth_put_object(f, OBJECT_FIELD, NULL, p);
if (r < 0)
return r;
-#endif
if (ret_object) {
r = journal_file_move_to_object(f, OBJECT_FIELD, p, ret_object);
@@ -1948,11 +1936,9 @@ static int journal_file_append_data(
if (r < 0)
return r;
-#if HAVE_GCRYPT
- r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
+ r = journal_file_auth_put_object(f, OBJECT_DATA, o, p);
if (r < 0)
return r;
-#endif
/* Create field object ... */
r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, NULL);
@@ -2198,11 +2184,9 @@ static int link_entry_into_array(
if (r < 0)
return r;
-#if HAVE_GCRYPT
- r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q);
+ r = journal_file_auth_put_object(f, OBJECT_ENTRY_ARRAY, o, q);
if (r < 0)
return r;
-#endif
write_entry_array_item(f, o, i, p);
@@ -2444,11 +2428,9 @@ static int journal_file_append_entry_internal(
for (size_t i = 0; i < n_items; i++)
write_entry_item(f, o, i, &items[i]);
-#if HAVE_GCRYPT
- r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np);
+ r = journal_file_auth_put_object(f, OBJECT_ENTRY, o, np);
if (r < 0)
return r;
-#endif
r = journal_file_link_entry(f, o, np, items, n_items);
if (r < 0)
@@ -2633,11 +2615,9 @@ int journal_file_append_entry(
else
machine_id = &_machine_id;
-#if HAVE_GCRYPT
- r = journal_file_maybe_append_tag(f, ts->realtime);
+ r = journal_file_auth_append_tag_maybe(f, ts->realtime);
if (r < 0)
return r;
-#endif
if (n_iovec < ALLOCA_MAX / sizeof(EntryItem) / 2)
items = newa(EntryItem, n_iovec);
@@ -4284,13 +4264,11 @@ int journal_file_open(
goto fail;
}
-#if HAVE_GCRYPT
if (!newly_created && journal_file_writable(f) && JOURNAL_HEADER_SEALED(f->header)) {
- r = journal_file_fss_load(f);
+ r = journal_file_auth_load(f);
if (r < 0)
goto fail;
}
-#endif
if (journal_file_writable(f)) {
if (metrics) {
@@ -4304,12 +4282,6 @@ int journal_file_open(
goto fail;
}
-#if HAVE_GCRYPT
- r = journal_file_hmac_setup(f);
- if (r < 0)
- goto fail;
-#endif
-
if (newly_created) {
r = journal_file_setup_field_hash_table(f);
if (r < 0)
@@ -4319,11 +4291,9 @@ int journal_file_open(
if (r < 0)
goto fail;
-#if HAVE_GCRYPT
- r = journal_file_append_first_tag(f);
+ r = journal_file_auth_append_tag_first(f);
if (r < 0)
goto fail;
-#endif
}
if (mmap_cache_fd_got_sigbus(f->cache_fd)) {
diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h
index fea181cafd..8499fa2b66 100644
--- a/src/libsystemd/sd-journal/journal-file.h
+++ b/src/libsystemd/sd-journal/journal-file.h
@@ -6,7 +6,6 @@
#include "compress.h"
#include "sd-forward.h"
-#include "gcrypt-util.h"
#include "journal-def.h"
#include "mmap-cache.h"
#include "sparse-endian.h"
@@ -98,17 +97,7 @@ typedef struct JournalFile {
void *compress_buffer;
#endif
- gcry_md_hd_t hmac;
- bool hmac_running;
-
- FSSHeader *fss_file;
- size_t fss_file_size;
-
- uint64_t fss_start_usec;
- uint64_t fss_interval_usec;
-
- struct iovec fsprg_state;
- struct iovec fsprg_seed;
+ JournalAuthContext *auth_context;
/* When we insert this file into the per-boot priority queue 'newest_by_boot_id' in sd_journal, then by these keys */
sd_id128_t newest_boot_id;
diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c
index 02420840df..dd3b6f32d4 100644
--- a/src/libsystemd/sd-journal/journal-verify.c
+++ b/src/libsystemd/sd-journal/journal-verify.c
@@ -9,8 +9,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
-#include "gcrypt-util.h"
-#include "journal-authenticate.h"
+#include "journal-authenticate-internal.h"
#include "journal-def.h"
#include "journal-file.h"
#include "journal-verify.h"
@@ -821,7 +820,7 @@ int journal_file_verify(
int r;
Object *o;
- uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_tag_realtime_end = 0;
+ uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0, last_tag_realtime_end = 0;
uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
usec_t min_entry_realtime = USEC_INFINITY, max_entry_realtime = 0;
sd_id128_t entry_boot_id = {}; /* Unnecessary initialization to appease gcc */
@@ -836,21 +835,20 @@ int journal_file_verify(
const char *tmp_dir = NULL;
MMapCache *m;
-#if HAVE_GCRYPT
- uint64_t last_tag = 0;
-#endif
assert(f);
if (key) {
-#if HAVE_GCRYPT
- r = journal_file_parse_verification_key(f, key);
+ r = journal_file_auth_load_key(f, key);
if (r < 0)
return log_error_errno(r, "Failed to load verification key: %m");
-#else
- return -EOPNOTSUPP;
-#endif
- } else if (JOURNAL_HEADER_SEALED(f->header))
- return -ENOKEY;
+ } else if (JOURNAL_HEADER_SEALED(f->header)) {
+ /* For a sealed journal file, request the verification key when journal sealing is supported.
+ * Otherwise, log that seal verification is skipped. */
+ if (journal_auth_supported())
+ return -ENOKEY;
+ else
+ log_notice("Journal file is sealed, but journal sealing support is disabled. Skipping seal verification.");
+ }
r = var_tmp_dir(&tmp_dir);
if (r < 0) {
@@ -1155,14 +1153,15 @@ int journal_file_verify(
}
}
-#if HAVE_GCRYPT
- if (JOURNAL_HEADER_SEALED(f->header)) {
+ if (JOURNAL_HEADER_SEALED(f->header) && journal_auth_supported()) {
uint64_t q, rt, rt_end;
debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
- rt = f->fss_start_usec + le64toh(o->tag.epoch) * f->fss_interval_usec;
- rt_end = usec_add(rt, f->fss_interval_usec);
+ r = journal_file_auth_epoch_to_realtime_usec(f, le64toh(o->tag.epoch), &rt, &rt_end);
+ if (r < 0)
+ goto fail;
+
if (entry_realtime_set && entry_realtime >= rt_end) {
error(p,
"tag/entry realtime timestamp out of synchronization (%"PRIu64" >= %"PRIu64")",
@@ -1192,16 +1191,16 @@ int journal_file_verify(
/* OK, now we know the epoch. So let's now set
* it, and calculate the HMAC for everything
* since the last tag. */
- r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
+ r = journal_file_auth_seek(f, le64toh(o->tag.epoch));
if (r < 0)
goto fail;
- r = journal_file_hmac_start(f);
+ r = journal_file_auth_start(f);
if (r < 0)
goto fail;
if (last_tag == 0) {
- r = journal_file_hmac_put_header(f);
+ r = journal_file_auth_put_header(f);
if (r < 0)
goto fail;
@@ -1214,7 +1213,7 @@ int journal_file_verify(
if (r < 0)
goto fail;
- r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q);
+ r = journal_file_auth_put_object(f, OBJECT_UNUSED, o, q);
if (r < 0)
goto fail;
@@ -1226,20 +1225,24 @@ int journal_file_verify(
if (r < 0)
goto fail;
- if (memcmp(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
+ uint8_t tag[TAG_LENGTH];
+ CLEANUP_ERASE(tag);
+
+ r = journal_file_auth_end(f, tag);
+ if (r < 0)
+ goto fail;
+
+ if (memcmp(o->tag.tag, tag, TAG_LENGTH) != 0) {
error(p, "Tag failed verification");
r = -EBADMSG;
goto fail;
}
- f->hmac_running = false;
last_tag_realtime = rt;
last_tag_realtime_end = rt_end;
}
last_tag = p + ALIGN64(le64toh(o->object.size));
-#endif
-
last_epoch = le64toh(o->tag.epoch);
n_tags++;
diff --git a/src/libsystemd/sd-journal/test-journal-verify.c b/src/libsystemd/sd-journal/test-journal-verify.c
index 2d797a18f3..6a1401764b 100644
--- a/src/libsystemd/sd-journal/test-journal-verify.c
+++ b/src/libsystemd/sd-journal/test-journal-verify.c
@@ -9,6 +9,7 @@
#include "chattr-util.h"
#include "fd-util.h"
#include "iovec-util.h"
+#include "journal-authenticate.h"
#include "journal-file-util.h"
#include "journal-verify.h"
#include "log.h"
@@ -174,6 +175,8 @@ int main(int argc, char *argv[]) {
const char *verification_key = NULL;
int max_iterations = 512;
+ journal_auth_init();
+
if (argc > 1) {
/* Don't limit the number of iterations when the verification key
* is provided on the command line, we want to do that only in CIs */
@@ -187,10 +190,9 @@ int main(int argc, char *argv[]) {
ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1));
run_test(verification_key, max_iterations);
-#if HAVE_GCRYPT
- /* If we're running without any arguments and we're compiled with gcrypt
+ /* If we're running without any arguments and journal sealing support is enabled,
* check the journal verification stuff with a valid key as well */
- if (argc <= 1) {
+ if (argc <= 1 && journal_auth_supported()) {
verification_key = "c262bd-85187f-0b1b04-877cc5/1c7af8-35a4e900";
ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1));
@@ -199,7 +201,6 @@ int main(int argc, char *argv[]) {
ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1));
run_test(verification_key, max_iterations);
}
-#endif
return 0;
}
diff --git a/src/libsystemd/sd-journal/test-journal.c b/src/libsystemd/sd-journal/test-journal.c
index bcd7293dba..645636d0d0 100644
--- a/src/libsystemd/sd-journal/test-journal.c
+++ b/src/libsystemd/sd-journal/test-journal.c
@@ -55,9 +55,8 @@ static void test_non_empty_one(void) {
iovec = IOVEC_MAKE_STRING(test);
ASSERT_OK_ZERO(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL, NULL));
-#if HAVE_GCRYPT
- journal_file_append_tag(f);
-#endif
+ journal_file_auth_append_tag(f);
+
journal_file_dump(f);
ASSERT_EQ(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p), 1);
@@ -200,9 +199,8 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
iovec = IOVEC_MAKE(data, data_size);
ASSERT_OK_ZERO(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL, NULL));
-#if HAVE_GCRYPT
- journal_file_append_tag(f);
-#endif
+ journal_file_auth_append_tag(f);
+
journal_file_dump(f);
/* We have to partially reimplement some of the dump logic, because the normal next_entry does the
@@ -590,6 +588,8 @@ static int intro(void) {
if (access("/etc/machine-id", F_OK) != 0)
return log_tests_skipped("/etc/machine-id not found");
+ journal_auth_init();
+
return EXIT_SUCCESS;
}
diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c
index 4277657195..5b1d6d8136 100644
--- a/src/shared/crypto-util.c
+++ b/src/shared/crypto-util.c
@@ -17,7 +17,6 @@
#include "strv.h"
#if HAVE_OPENSSL
-# include
# include
# include
# include
@@ -37,14 +36,14 @@ struct OpenSSLAskPasswordUI {
#endif
};
-static DLSYM_PROTOTYPE(ASN1_INTEGER_dup) = NULL;
-static DLSYM_PROTOTYPE(ASN1_INTEGER_free) = NULL;
-static DLSYM_PROTOTYPE(ASN1_INTEGER_set) = NULL;
DLSYM_PROTOTYPE(ASN1_ANY_it) = NULL;
DLSYM_PROTOTYPE(ASN1_BIT_STRING_it) = NULL;
DLSYM_PROTOTYPE(ASN1_BMPSTRING_it) = NULL;
DLSYM_PROTOTYPE(ASN1_BMPSTRING_new) = NULL;
DLSYM_PROTOTYPE(ASN1_IA5STRING_it) = NULL;
+static DLSYM_PROTOTYPE(ASN1_INTEGER_dup) = NULL;
+static DLSYM_PROTOTYPE(ASN1_INTEGER_free) = NULL;
+static DLSYM_PROTOTYPE(ASN1_INTEGER_set) = NULL;
DLSYM_PROTOTYPE(ASN1_OBJECT_it) = NULL;
DLSYM_PROTOTYPE(ASN1_OCTET_STRING_free) = NULL;
DLSYM_PROTOTYPE(ASN1_OCTET_STRING_it) = NULL;
@@ -73,12 +72,32 @@ DLSYM_PROTOTYPE(BIO_s_mem) = NULL;
DLSYM_PROTOTYPE(BIO_write) = NULL;
DLSYM_PROTOTYPE(BN_CTX_free) = NULL;
DLSYM_PROTOTYPE(BN_CTX_new) = NULL;
+DLSYM_PROTOTYPE(BN_CTX_secure_new) = NULL;
+DLSYM_PROTOTYPE(BN_add) = NULL;
+DLSYM_PROTOTYPE(BN_add_word) = NULL;
DLSYM_PROTOTYPE(BN_bin2bn) = NULL;
-static DLSYM_PROTOTYPE(BN_bn2bin) = NULL;
+DLSYM_PROTOTYPE(BN_bn2bin) = NULL;
+DLSYM_PROTOTYPE(BN_bn2binpad) = NULL;
DLSYM_PROTOTYPE(BN_bn2nativepad) = NULL;
+DLSYM_PROTOTYPE(BN_check_prime) = NULL;
+DLSYM_PROTOTYPE(BN_clear_free) = NULL;
+DLSYM_PROTOTYPE(BN_cmp) = NULL;
+DLSYM_PROTOTYPE(BN_copy) = NULL;
DLSYM_PROTOTYPE(BN_free) = NULL;
+DLSYM_PROTOTYPE(BN_is_negative) = NULL;
+DLSYM_PROTOTYPE(BN_mod_exp) = NULL;
+DLSYM_PROTOTYPE(BN_mod_inverse) = NULL;
+DLSYM_PROTOTYPE(BN_mod_lshift1_quick) = NULL;
+DLSYM_PROTOTYPE(BN_mod_mul) = NULL;
+DLSYM_PROTOTYPE(BN_mod_sqr) = NULL;
+DLSYM_PROTOTYPE(BN_mod_sub) = NULL;
+DLSYM_PROTOTYPE(BN_mul) = NULL;
DLSYM_PROTOTYPE(BN_new) = NULL;
+DLSYM_PROTOTYPE(BN_nnmod) = NULL;
DLSYM_PROTOTYPE(BN_num_bits) = NULL;
+DLSYM_PROTOTYPE(BN_secure_new) = NULL;
+DLSYM_PROTOTYPE(BN_set_word) = NULL;
+DLSYM_PROTOTYPE(BN_sub_word) = NULL;
DLSYM_PROTOTYPE(CRYPTO_free) = NULL;
DLSYM_PROTOTYPE(ECDSA_SIG_free) = NULL;
DLSYM_PROTOTYPE(EC_GROUP_free) = NULL;
@@ -130,12 +149,13 @@ static DLSYM_PROTOTYPE(EVP_KDF_fetch) = NULL;
static DLSYM_PROTOTYPE(EVP_KDF_free) = NULL;
DLSYM_PROTOTYPE(EVP_MAC_CTX_free) = NULL;
static DLSYM_PROTOTYPE(EVP_MAC_CTX_get_mac_size) = NULL;
-static DLSYM_PROTOTYPE(EVP_MAC_CTX_new) = NULL;
-static DLSYM_PROTOTYPE(EVP_MAC_fetch) = NULL;
-static DLSYM_PROTOTYPE(EVP_MAC_final) = NULL;
+DLSYM_PROTOTYPE(EVP_MAC_CTX_new) = NULL;
+DLSYM_PROTOTYPE(EVP_MAC_fetch) = NULL;
+DLSYM_PROTOTYPE(EVP_MAC_final) = NULL;
DLSYM_PROTOTYPE(EVP_MAC_free) = NULL;
-static DLSYM_PROTOTYPE(EVP_MAC_init) = NULL;
-static DLSYM_PROTOTYPE(EVP_MAC_update) = NULL;
+DLSYM_PROTOTYPE(EVP_MAC_init) = NULL;
+DLSYM_PROTOTYPE(EVP_MAC_update) = NULL;
+DLSYM_PROTOTYPE(EVP_MD_CTX_copy_ex) = NULL;
DLSYM_PROTOTYPE(EVP_MD_CTX_free) = NULL;
DLSYM_PROTOTYPE(EVP_MD_CTX_get0_md) = NULL;
DLSYM_PROTOTYPE(EVP_MD_CTX_new) = NULL;
@@ -185,8 +205,6 @@ DLSYM_PROTOTYPE(EVP_sha256) = NULL;
DLSYM_PROTOTYPE(EVP_sha384) = NULL;
DLSYM_PROTOTYPE(EVP_sha512) = NULL;
DLSYM_PROTOTYPE(HMAC) = NULL;
-DLSYM_PROTOTYPE(SHA1) = NULL;
-DLSYM_PROTOTYPE(SHA512) = NULL;
DLSYM_PROTOTYPE(OBJ_nid2obj) = NULL;
DLSYM_PROTOTYPE(OBJ_nid2sn) = NULL;
DLSYM_PROTOTYPE(OBJ_sn2nid) = NULL;
@@ -197,11 +215,11 @@ DLSYM_PROTOTYPE(OPENSSL_sk_pop_free) = NULL;
DLSYM_PROTOTYPE(OPENSSL_sk_push) = NULL;
DLSYM_PROTOTYPE(OPENSSL_sk_value) = NULL;
DLSYM_PROTOTYPE(OSSL_EC_curve_nid2name) = NULL;
-static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new) = NULL;
-static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free) = NULL;
+DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free) = NULL;
+DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new) = NULL;
static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_octet_string) = NULL;
-static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string) = NULL;
-static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param) = NULL;
+DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string) = NULL;
+DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param) = NULL;
DLSYM_PROTOTYPE(OSSL_PARAM_construct_BN) = NULL;
DLSYM_PROTOTYPE(OSSL_PARAM_construct_end) = NULL;
DLSYM_PROTOTYPE(OSSL_PARAM_construct_octet_string) = NULL;
@@ -220,12 +238,12 @@ DLSYM_PROTOTYPE(PEM_read_PrivateKey) = NULL;
DLSYM_PROTOTYPE(PEM_read_X509) = NULL;
static DLSYM_PROTOTYPE(PEM_read_bio_PrivateKey) = NULL;
static DLSYM_PROTOTYPE(PEM_read_bio_X509) = NULL;
-DLSYM_PROTOTYPE(PKCS5_PBKDF2_HMAC) = NULL;
DLSYM_PROTOTYPE(PEM_write_PUBKEY) = NULL;
DLSYM_PROTOTYPE(PEM_write_PrivateKey) = NULL;
DLSYM_PROTOTYPE(PEM_write_X509) = NULL;
-DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free) = NULL;
+DLSYM_PROTOTYPE(PKCS5_PBKDF2_HMAC) = NULL;
DLSYM_PROTOTYPE(PKCS7_ATTR_SIGN_it) = NULL;
+DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free) = NULL;
static DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_new) = NULL;
static DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_set) = NULL;
DLSYM_PROTOTYPE(PKCS7_add0_attrib_signing_time) = NULL;
@@ -246,22 +264,24 @@ DLSYM_PROTOTYPE(PKCS7_set_content) = NULL;
static DLSYM_PROTOTYPE(PKCS7_set_type) = NULL;
DLSYM_PROTOTYPE(PKCS7_sign) = NULL;
DLSYM_PROTOTYPE(PKCS7_verify) = NULL;
-static DLSYM_PROTOTYPE(X509_ALGOR_set0) = NULL;
-DLSYM_PROTOTYPE(X509_NAME_free) = NULL;
+DLSYM_PROTOTYPE(SHA1) = NULL;
+DLSYM_PROTOTYPE(SHA512) = NULL;
DLSYM_PROTOTYPE(X509_ALGOR_free) = NULL;
+static DLSYM_PROTOTYPE(X509_ALGOR_set0) = NULL;
DLSYM_PROTOTYPE(X509_ATTRIBUTE_free) = NULL;
+DLSYM_PROTOTYPE(X509_NAME_free) = NULL;
DLSYM_PROTOTYPE(X509_NAME_oneline) = NULL;
static DLSYM_PROTOTYPE(X509_NAME_set) = NULL;
DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_host) = NULL;
DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_ip) = NULL;
DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set_hostflags) = NULL;
DLSYM_PROTOTYPE(X509_free) = NULL;
-DLSYM_PROTOTYPE(X509_gmtime_adj) = NULL;
static DLSYM_PROTOTYPE(X509_get0_serialNumber) = NULL;
static DLSYM_PROTOTYPE(X509_get_issuer_name) = NULL;
DLSYM_PROTOTYPE(X509_get_pubkey) = NULL;
static DLSYM_PROTOTYPE(X509_get_signature_info) = NULL;
DLSYM_PROTOTYPE(X509_get_subject_name) = NULL;
+DLSYM_PROTOTYPE(X509_gmtime_adj) = NULL;
DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING) = NULL;
DLSYM_PROTOTYPE(d2i_ECPKParameters) = NULL;
DLSYM_PROTOTYPE(d2i_PKCS7) = NULL;
@@ -276,7 +296,6 @@ static DLSYM_PROTOTYPE(i2d_PublicKey) = NULL;
DLSYM_PROTOTYPE(i2d_X509) = NULL;
DLSYM_PROTOTYPE(i2d_X509_NAME) = NULL;
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_PARAM_BLD*, sym_OSSL_PARAM_BLD_free, OSSL_PARAM_BLD_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_STORE_CTX*, sym_OSSL_STORE_close, OSSL_STORE_closep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_STORE_INFO*, sym_OSSL_STORE_INFO_free, OSSL_STORE_INFO_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_KDF*, sym_EVP_KDF_free, EVP_KDF_freep, NULL);
@@ -304,11 +323,11 @@ DLSYM_PROTOTYPE(EC_KEY_new) = NULL;
DLSYM_PROTOTYPE(EC_KEY_set_group) = NULL;
DLSYM_PROTOTYPE(EC_KEY_set_public_key) = NULL;
DLSYM_PROTOTYPE(EVP_PKEY_assign) = NULL;
+DLSYM_PROTOTYPE(RSAPublicKey_dup) = NULL;
DLSYM_PROTOTYPE(RSA_free) = NULL;
DLSYM_PROTOTYPE(RSA_new) = NULL;
DLSYM_PROTOTYPE(RSA_set0_key) = NULL;
DLSYM_PROTOTYPE(RSA_size) = NULL;
-DLSYM_PROTOTYPE(RSAPublicKey_dup) = NULL;
REENABLE_WARNING;
#endif
@@ -348,15 +367,10 @@ int dlopen_libcrypto(int log_level) {
DLSYM_ARG(ASN1_BIT_STRING_it),
DLSYM_ARG(ASN1_BMPSTRING_it),
DLSYM_ARG(ASN1_BMPSTRING_new),
- DLSYM_ARG(ASN1_get_object),
DLSYM_ARG(ASN1_IA5STRING_it),
DLSYM_ARG(ASN1_INTEGER_dup),
DLSYM_ARG(ASN1_INTEGER_free),
DLSYM_ARG(ASN1_INTEGER_set),
- DLSYM_ARG(ASN1_item_d2i),
- DLSYM_ARG(ASN1_item_free),
- DLSYM_ARG(ASN1_item_i2d),
- DLSYM_ARG(ASN1_item_new),
DLSYM_ARG(ASN1_OBJECT_it),
DLSYM_ARG(ASN1_OCTET_STRING_free),
DLSYM_ARG(ASN1_OCTET_STRING_it),
@@ -369,35 +383,56 @@ int dlopen_libcrypto(int log_level) {
DLSYM_ARG(ASN1_TIME_free),
DLSYM_ARG(ASN1_TIME_set),
DLSYM_ARG(ASN1_TYPE_new),
+ DLSYM_ARG(ASN1_get_object),
+ DLSYM_ARG(ASN1_item_d2i),
+ DLSYM_ARG(ASN1_item_free),
+ DLSYM_ARG(ASN1_item_i2d),
+ DLSYM_ARG(ASN1_item_new),
DLSYM_ARG(BIO_ctrl),
DLSYM_ARG(BIO_find_type),
- DLSYM_ARG(BIO_free_all),
DLSYM_ARG(BIO_free),
+ DLSYM_ARG(BIO_free_all),
+ DLSYM_ARG(BIO_new),
DLSYM_ARG(BIO_new_mem_buf),
DLSYM_ARG(BIO_new_socket),
- DLSYM_ARG(BIO_new),
DLSYM_ARG(BIO_s_mem),
DLSYM_ARG(BIO_write),
- DLSYM_ARG(BN_bin2bn),
- DLSYM_ARG(BN_bn2bin),
- DLSYM_ARG(BN_bn2nativepad),
DLSYM_ARG(BN_CTX_free),
DLSYM_ARG(BN_CTX_new),
+ DLSYM_ARG(BN_CTX_secure_new),
+ DLSYM_ARG(BN_add),
+ DLSYM_ARG(BN_add_word),
+ DLSYM_ARG(BN_bin2bn),
+ DLSYM_ARG(BN_bn2bin),
+ DLSYM_ARG(BN_bn2binpad),
+ DLSYM_ARG(BN_bn2nativepad),
+ DLSYM_ARG(BN_check_prime),
+ DLSYM_ARG(BN_clear_free),
+ DLSYM_ARG(BN_cmp),
+ DLSYM_ARG(BN_copy),
DLSYM_ARG(BN_free),
+ DLSYM_ARG(BN_is_negative),
+ DLSYM_ARG(BN_mod_exp),
+ DLSYM_ARG(BN_mod_inverse),
+ DLSYM_ARG(BN_mod_lshift1_quick),
+ DLSYM_ARG(BN_mod_mul),
+ DLSYM_ARG(BN_mod_sqr),
+ DLSYM_ARG(BN_mod_sub),
+ DLSYM_ARG(BN_mul),
DLSYM_ARG(BN_new),
+ DLSYM_ARG(BN_nnmod),
DLSYM_ARG(BN_num_bits),
+ DLSYM_ARG(BN_secure_new),
+ DLSYM_ARG(BN_set_word),
+ DLSYM_ARG(BN_sub_word),
DLSYM_ARG(CRYPTO_free),
- DLSYM_ARG(d2i_ASN1_OCTET_STRING),
- DLSYM_ARG(d2i_ECPKParameters),
- DLSYM_ARG(d2i_PKCS7),
- DLSYM_ARG(d2i_PUBKEY),
- DLSYM_ARG(d2i_X509),
+ DLSYM_ARG(ECDSA_SIG_free),
DLSYM_ARG(EC_GROUP_free),
- DLSYM_ARG(EC_GROUP_get_curve_name),
- DLSYM_ARG(EC_GROUP_get_curve),
- DLSYM_ARG(EC_GROUP_get_field_type),
DLSYM_ARG(EC_GROUP_get0_generator),
DLSYM_ARG(EC_GROUP_get0_order),
+ DLSYM_ARG(EC_GROUP_get_curve),
+ DLSYM_ARG(EC_GROUP_get_curve_name),
+ DLSYM_ARG(EC_GROUP_get_field_type),
DLSYM_ARG(EC_GROUP_new_by_curve_name),
DLSYM_ARG(EC_POINT_free),
DLSYM_ARG(EC_POINT_new),
@@ -405,36 +440,11 @@ int dlopen_libcrypto(int log_level) {
DLSYM_ARG(EC_POINT_point2buf),
DLSYM_ARG(EC_POINT_point2oct),
DLSYM_ARG(EC_POINT_set_affine_coordinates),
- DLSYM_ARG(ECDSA_SIG_free),
-#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
- DLSYM_ARG_FORCE(ENGINE_by_id),
- DLSYM_ARG_FORCE(ENGINE_free),
- DLSYM_ARG_FORCE(ENGINE_init),
- DLSYM_ARG_FORCE(ENGINE_load_private_key),
-#endif
-#if !defined(OPENSSL_NO_DEPRECATED_3_0)
- DLSYM_ARG_FORCE(EC_KEY_check_key),
- DLSYM_ARG_FORCE(EC_KEY_free),
- DLSYM_ARG_FORCE(EC_KEY_new),
- DLSYM_ARG_FORCE(EC_KEY_set_group),
- DLSYM_ARG_FORCE(EC_KEY_set_public_key),
- DLSYM_ARG_FORCE(ECDSA_do_verify),
- DLSYM_ARG_FORCE(ECDSA_SIG_new),
- DLSYM_ARG_FORCE(ECDSA_SIG_set0),
- DLSYM_ARG_FORCE(EVP_PKEY_assign),
- DLSYM_ARG_FORCE(RSA_free),
- DLSYM_ARG_FORCE(RSA_new),
- DLSYM_ARG_FORCE(RSA_set0_key),
- DLSYM_ARG_FORCE(RSA_size),
- DLSYM_ARG_FORCE(RSAPublicKey_dup),
-#endif
DLSYM_ARG(ERR_clear_error),
- DLSYM_ARG(ERR_error_string_n),
DLSYM_ARG(ERR_error_string),
+ DLSYM_ARG(ERR_error_string_n),
DLSYM_ARG(ERR_get_error),
DLSYM_ARG(ERR_peek_last_error),
- DLSYM_ARG(EVP_aes_256_ctr),
- DLSYM_ARG(EVP_aes_256_gcm),
DLSYM_ARG(EVP_CIPHER_CTX_ctrl),
DLSYM_ARG(EVP_CIPHER_CTX_free),
DLSYM_ARG(EVP_CIPHER_CTX_get_block_size),
@@ -456,11 +466,9 @@ int dlopen_libcrypto(int log_level) {
DLSYM_ARG(EVP_DigestVerify),
DLSYM_ARG(EVP_DigestVerifyInit),
DLSYM_ARG(EVP_EncryptFinal_ex),
- DLSYM_ARG(EVP_EncryptInit_ex),
DLSYM_ARG(EVP_EncryptInit),
+ DLSYM_ARG(EVP_EncryptInit_ex),
DLSYM_ARG(EVP_EncryptUpdate),
- DLSYM_ARG(EVP_get_cipherbyname),
- DLSYM_ARG(EVP_get_digestbyname),
DLSYM_ARG(EVP_KDF_CTX_free),
DLSYM_ARG(EVP_KDF_CTX_new),
DLSYM_ARG(EVP_KDF_derive),
@@ -474,59 +482,56 @@ int dlopen_libcrypto(int log_level) {
DLSYM_ARG(EVP_MAC_free),
DLSYM_ARG(EVP_MAC_init),
DLSYM_ARG(EVP_MAC_update),
+ DLSYM_ARG(EVP_MD_CTX_copy_ex),
DLSYM_ARG(EVP_MD_CTX_free),
DLSYM_ARG(EVP_MD_CTX_get0_md),
DLSYM_ARG(EVP_MD_CTX_new),
DLSYM_ARG(EVP_MD_CTX_set_pkey_ctx),
DLSYM_ARG(EVP_MD_fetch),
DLSYM_ARG(EVP_MD_free),
+ DLSYM_ARG(EVP_MD_get0_name),
DLSYM_ARG(EVP_MD_get_size),
DLSYM_ARG(EVP_MD_get_type),
- DLSYM_ARG(EVP_MD_get0_name),
DLSYM_ARG(EVP_PKEY_CTX_free),
+ DLSYM_ARG(EVP_PKEY_CTX_new),
DLSYM_ARG(EVP_PKEY_CTX_new_from_name),
DLSYM_ARG(EVP_PKEY_CTX_new_id),
- DLSYM_ARG(EVP_PKEY_CTX_new),
+ DLSYM_ARG(EVP_PKEY_CTX_set0_rsa_oaep_label),
DLSYM_ARG(EVP_PKEY_CTX_set_ec_paramgen_curve_nid),
DLSYM_ARG(EVP_PKEY_CTX_set_rsa_oaep_md),
DLSYM_ARG(EVP_PKEY_CTX_set_rsa_padding),
DLSYM_ARG(EVP_PKEY_CTX_set_signature_md),
- DLSYM_ARG(EVP_PKEY_CTX_set0_rsa_oaep_label),
+ DLSYM_ARG(EVP_PKEY_derive),
DLSYM_ARG(EVP_PKEY_derive_init),
DLSYM_ARG(EVP_PKEY_derive_set_peer),
- DLSYM_ARG(EVP_PKEY_derive),
- DLSYM_ARG(EVP_PKEY_encrypt_init),
DLSYM_ARG(EVP_PKEY_encrypt),
+ DLSYM_ARG(EVP_PKEY_encrypt_init),
DLSYM_ARG(EVP_PKEY_eq),
DLSYM_ARG(EVP_PKEY_free),
- DLSYM_ARG(EVP_PKEY_fromdata_init),
DLSYM_ARG(EVP_PKEY_fromdata),
+ DLSYM_ARG(EVP_PKEY_fromdata_init),
+ DLSYM_ARG(EVP_PKEY_get1_encoded_public_key),
DLSYM_ARG(EVP_PKEY_get_base_id),
DLSYM_ARG(EVP_PKEY_get_bits),
DLSYM_ARG(EVP_PKEY_get_bn_param),
DLSYM_ARG(EVP_PKEY_get_group_name),
DLSYM_ARG(EVP_PKEY_get_id),
DLSYM_ARG(EVP_PKEY_get_utf8_string_param),
- DLSYM_ARG(EVP_PKEY_get1_encoded_public_key),
- DLSYM_ARG(EVP_PKEY_keygen_init),
DLSYM_ARG(EVP_PKEY_keygen),
- DLSYM_ARG(EVP_PKEY_new_raw_public_key),
+ DLSYM_ARG(EVP_PKEY_keygen_init),
DLSYM_ARG(EVP_PKEY_new),
- DLSYM_ARG(EVP_PKEY_verify_init),
+ DLSYM_ARG(EVP_PKEY_new_raw_public_key),
DLSYM_ARG(EVP_PKEY_verify),
+ DLSYM_ARG(EVP_PKEY_verify_init),
+ DLSYM_ARG(EVP_aes_256_ctr),
+ DLSYM_ARG(EVP_aes_256_gcm),
+ DLSYM_ARG(EVP_get_cipherbyname),
+ DLSYM_ARG(EVP_get_digestbyname),
DLSYM_ARG(EVP_sha1),
DLSYM_ARG(EVP_sha256),
DLSYM_ARG(EVP_sha384),
DLSYM_ARG(EVP_sha512),
DLSYM_ARG(HMAC),
- DLSYM_ARG(i2d_ASN1_INTEGER),
- DLSYM_ARG(i2d_PKCS7_fp),
- DLSYM_ARG(i2d_PKCS7),
- DLSYM_ARG(i2d_PUBKEY_fp),
- DLSYM_ARG(i2d_PUBKEY),
- DLSYM_ARG(i2d_PublicKey),
- DLSYM_ARG(i2d_X509_NAME),
- DLSYM_ARG(i2d_X509),
DLSYM_ARG(OBJ_nid2obj),
DLSYM_ARG(OBJ_nid2sn),
DLSYM_ARG(OBJ_sn2nid),
@@ -548,30 +553,33 @@ int dlopen_libcrypto(int log_level) {
DLSYM_ARG(OSSL_PARAM_construct_utf8_string),
DLSYM_ARG(OSSL_PARAM_free),
DLSYM_ARG(OSSL_PROVIDER_try_load),
- DLSYM_ARG(OSSL_STORE_close),
- DLSYM_ARG(OSSL_STORE_expect),
DLSYM_ARG(OSSL_STORE_INFO_free),
DLSYM_ARG(OSSL_STORE_INFO_get1_CERT),
DLSYM_ARG(OSSL_STORE_INFO_get1_PKEY),
+ DLSYM_ARG(OSSL_STORE_close),
+ DLSYM_ARG(OSSL_STORE_expect),
DLSYM_ARG(OSSL_STORE_load),
DLSYM_ARG(OSSL_STORE_open),
+ DLSYM_ARG(PEM_read_PUBKEY),
+ DLSYM_ARG(PEM_read_PrivateKey),
+ DLSYM_ARG(PEM_read_X509),
DLSYM_ARG(PEM_read_bio_PrivateKey),
DLSYM_ARG(PEM_read_bio_X509),
- DLSYM_ARG(PEM_read_PrivateKey),
- DLSYM_ARG(PEM_read_PUBKEY),
- DLSYM_ARG(PEM_read_X509),
- DLSYM_ARG(PEM_write_PrivateKey),
DLSYM_ARG(PEM_write_PUBKEY),
+ DLSYM_ARG(PEM_write_PrivateKey),
DLSYM_ARG(PEM_write_X509),
DLSYM_ARG(PKCS5_PBKDF2_HMAC),
+ DLSYM_ARG(PKCS7_ATTR_SIGN_it),
+ DLSYM_ARG(PKCS7_SIGNER_INFO_free),
+ DLSYM_ARG(PKCS7_SIGNER_INFO_new),
+ DLSYM_ARG(PKCS7_SIGNER_INFO_set),
+ DLSYM_ARG(PKCS7_add0_attrib_signing_time),
+ DLSYM_ARG(PKCS7_add1_attrib_digest),
DLSYM_ARG(PKCS7_add_attrib_content_type),
DLSYM_ARG(PKCS7_add_attrib_smimecap),
DLSYM_ARG(PKCS7_add_certificate),
DLSYM_ARG(PKCS7_add_signed_attribute),
DLSYM_ARG(PKCS7_add_signer),
- DLSYM_ARG(PKCS7_add0_attrib_signing_time),
- DLSYM_ARG(PKCS7_add1_attrib_digest),
- DLSYM_ARG(PKCS7_ATTR_SIGN_it),
DLSYM_ARG(PKCS7_content_new),
DLSYM_ARG(PKCS7_ctrl),
DLSYM_ARG(PKCS7_dataFinal),
@@ -582,43 +590,76 @@ int dlopen_libcrypto(int log_level) {
DLSYM_ARG(PKCS7_set_content),
DLSYM_ARG(PKCS7_set_type),
DLSYM_ARG(PKCS7_sign),
- DLSYM_ARG(PKCS7_SIGNER_INFO_free),
- DLSYM_ARG(PKCS7_SIGNER_INFO_new),
- DLSYM_ARG(PKCS7_SIGNER_INFO_set),
DLSYM_ARG(PKCS7_verify),
DLSYM_ARG(SHA1),
DLSYM_ARG(SHA512),
-#ifndef OPENSSL_NO_UI_CONSOLE
- DLSYM_ARG(UI_create_method),
- DLSYM_ARG(UI_destroy_method),
- DLSYM_ARG(UI_get_default_method),
- DLSYM_ARG(UI_get_method),
- DLSYM_ARG(UI_get_string_type),
- DLSYM_ARG(UI_get0_output_string),
- DLSYM_ARG(UI_method_get_ex_data),
- DLSYM_ARG(UI_method_get_reader),
- DLSYM_ARG(UI_method_set_ex_data),
- DLSYM_ARG(UI_method_set_reader),
- DLSYM_ARG(UI_OpenSSL),
- DLSYM_ARG(UI_set_default_method),
- DLSYM_ARG(UI_set_result),
-#endif
DLSYM_ARG(X509_ALGOR_free),
DLSYM_ARG(X509_ALGOR_set0),
DLSYM_ARG(X509_ATTRIBUTE_free),
+ DLSYM_ARG(X509_NAME_free),
+ DLSYM_ARG(X509_NAME_oneline),
+ DLSYM_ARG(X509_NAME_set),
+ DLSYM_ARG(X509_VERIFY_PARAM_set1_host),
+ DLSYM_ARG(X509_VERIFY_PARAM_set1_ip),
+ DLSYM_ARG(X509_VERIFY_PARAM_set_hostflags),
DLSYM_ARG(X509_free),
+ DLSYM_ARG(X509_get0_serialNumber),
DLSYM_ARG(X509_get_issuer_name),
DLSYM_ARG(X509_get_pubkey),
DLSYM_ARG(X509_get_signature_info),
DLSYM_ARG(X509_get_subject_name),
- DLSYM_ARG(X509_get0_serialNumber),
DLSYM_ARG(X509_gmtime_adj),
- DLSYM_ARG(X509_NAME_free),
- DLSYM_ARG(X509_NAME_oneline),
- DLSYM_ARG(X509_NAME_set),
- DLSYM_ARG(X509_VERIFY_PARAM_set_hostflags),
- DLSYM_ARG(X509_VERIFY_PARAM_set1_host),
- DLSYM_ARG(X509_VERIFY_PARAM_set1_ip));
+ DLSYM_ARG(d2i_ASN1_OCTET_STRING),
+ DLSYM_ARG(d2i_ECPKParameters),
+ DLSYM_ARG(d2i_PKCS7),
+ DLSYM_ARG(d2i_PUBKEY),
+ DLSYM_ARG(d2i_X509),
+ DLSYM_ARG(i2d_ASN1_INTEGER),
+ DLSYM_ARG(i2d_PKCS7),
+ DLSYM_ARG(i2d_PKCS7_fp),
+ DLSYM_ARG(i2d_PUBKEY),
+ DLSYM_ARG(i2d_PUBKEY_fp),
+ DLSYM_ARG(i2d_PublicKey),
+ DLSYM_ARG(i2d_X509),
+ DLSYM_ARG(i2d_X509_NAME),
+#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+ DLSYM_ARG_FORCE(ENGINE_by_id),
+ DLSYM_ARG_FORCE(ENGINE_free),
+ DLSYM_ARG_FORCE(ENGINE_init),
+ DLSYM_ARG_FORCE(ENGINE_load_private_key),
+#endif
+#if !defined(OPENSSL_NO_DEPRECATED_3_0)
+ DLSYM_ARG_FORCE(ECDSA_SIG_new),
+ DLSYM_ARG_FORCE(ECDSA_SIG_set0),
+ DLSYM_ARG_FORCE(ECDSA_do_verify),
+ DLSYM_ARG_FORCE(EC_KEY_check_key),
+ DLSYM_ARG_FORCE(EC_KEY_free),
+ DLSYM_ARG_FORCE(EC_KEY_new),
+ DLSYM_ARG_FORCE(EC_KEY_set_group),
+ DLSYM_ARG_FORCE(EC_KEY_set_public_key),
+ DLSYM_ARG_FORCE(EVP_PKEY_assign),
+ DLSYM_ARG_FORCE(RSAPublicKey_dup),
+ DLSYM_ARG_FORCE(RSA_free),
+ DLSYM_ARG_FORCE(RSA_new),
+ DLSYM_ARG_FORCE(RSA_set0_key),
+ DLSYM_ARG_FORCE(RSA_size),
+#endif
+#ifndef OPENSSL_NO_UI_CONSOLE
+ DLSYM_ARG(UI_OpenSSL),
+ DLSYM_ARG(UI_create_method),
+ DLSYM_ARG(UI_destroy_method),
+ DLSYM_ARG(UI_get0_output_string),
+ DLSYM_ARG(UI_get_default_method),
+ DLSYM_ARG(UI_get_method),
+ DLSYM_ARG(UI_get_string_type),
+ DLSYM_ARG(UI_method_get_ex_data),
+ DLSYM_ARG(UI_method_get_reader),
+ DLSYM_ARG(UI_method_set_ex_data),
+ DLSYM_ARG(UI_method_set_reader),
+ DLSYM_ARG(UI_set_default_method),
+ DLSYM_ARG(UI_set_result),
+#endif
+ NULL);
if (r >= 0)
break;
}
diff --git a/src/shared/crypto-util.h b/src/shared/crypto-util.h
index 00e0b4f452..bd7dda0931 100644
--- a/src/shared/crypto-util.h
+++ b/src/shared/crypto-util.h
@@ -47,6 +47,7 @@ int dlopen_libcrypto(int log_level);
# include /* IWYU pragma: export */
# include /* IWYU pragma: export */
+# include /* IWYU pragma: export */
# include /* IWYU pragma: export */
# include /* IWYU pragma: export */
# include /* IWYU pragma: export */
@@ -64,12 +65,7 @@ extern DLSYM_PROTOTYPE(ASN1_ANY_it);
extern DLSYM_PROTOTYPE(ASN1_BIT_STRING_it);
extern DLSYM_PROTOTYPE(ASN1_BMPSTRING_it);
extern DLSYM_PROTOTYPE(ASN1_BMPSTRING_new);
-extern DLSYM_PROTOTYPE(ASN1_get_object);
extern DLSYM_PROTOTYPE(ASN1_IA5STRING_it);
-extern DLSYM_PROTOTYPE(ASN1_item_d2i);
-extern DLSYM_PROTOTYPE(ASN1_item_free);
-extern DLSYM_PROTOTYPE(ASN1_item_i2d);
-extern DLSYM_PROTOTYPE(ASN1_item_new);
extern DLSYM_PROTOTYPE(ASN1_OBJECT_it);
extern DLSYM_PROTOTYPE(ASN1_OCTET_STRING_free);
extern DLSYM_PROTOTYPE(ASN1_OCTET_STRING_it);
@@ -82,46 +78,65 @@ extern DLSYM_PROTOTYPE(ASN1_STRING_set0);
extern DLSYM_PROTOTYPE(ASN1_TIME_free);
extern DLSYM_PROTOTYPE(ASN1_TIME_set);
extern DLSYM_PROTOTYPE(ASN1_TYPE_new);
+extern DLSYM_PROTOTYPE(ASN1_get_object);
+extern DLSYM_PROTOTYPE(ASN1_item_d2i);
+extern DLSYM_PROTOTYPE(ASN1_item_free);
+extern DLSYM_PROTOTYPE(ASN1_item_i2d);
+extern DLSYM_PROTOTYPE(ASN1_item_new);
extern DLSYM_PROTOTYPE(BIO_ctrl);
extern DLSYM_PROTOTYPE(BIO_find_type);
-extern DLSYM_PROTOTYPE(BIO_free_all);
extern DLSYM_PROTOTYPE(BIO_free);
+extern DLSYM_PROTOTYPE(BIO_free_all);
+extern DLSYM_PROTOTYPE(BIO_new);
extern DLSYM_PROTOTYPE(BIO_new_mem_buf);
extern DLSYM_PROTOTYPE(BIO_new_socket);
-extern DLSYM_PROTOTYPE(BIO_new);
extern DLSYM_PROTOTYPE(BIO_s_mem);
extern DLSYM_PROTOTYPE(BIO_write);
-extern DLSYM_PROTOTYPE(BN_bin2bn);
-extern DLSYM_PROTOTYPE(BN_bn2nativepad);
extern DLSYM_PROTOTYPE(BN_CTX_free);
extern DLSYM_PROTOTYPE(BN_CTX_new);
+extern DLSYM_PROTOTYPE(BN_CTX_secure_new);
+extern DLSYM_PROTOTYPE(BN_add);
+extern DLSYM_PROTOTYPE(BN_add_word);
+extern DLSYM_PROTOTYPE(BN_bin2bn);
+extern DLSYM_PROTOTYPE(BN_bn2bin);
+extern DLSYM_PROTOTYPE(BN_bn2binpad);
+extern DLSYM_PROTOTYPE(BN_bn2nativepad);
+extern DLSYM_PROTOTYPE(BN_check_prime);
+extern DLSYM_PROTOTYPE(BN_clear_free);
+extern DLSYM_PROTOTYPE(BN_cmp);
+extern DLSYM_PROTOTYPE(BN_copy);
extern DLSYM_PROTOTYPE(BN_free);
+extern DLSYM_PROTOTYPE(BN_is_negative);
+extern DLSYM_PROTOTYPE(BN_mod_exp);
+extern DLSYM_PROTOTYPE(BN_mod_inverse);
+extern DLSYM_PROTOTYPE(BN_mod_lshift1_quick);
+extern DLSYM_PROTOTYPE(BN_mod_mul);
+extern DLSYM_PROTOTYPE(BN_mod_sqr);
+extern DLSYM_PROTOTYPE(BN_mod_sub);
+extern DLSYM_PROTOTYPE(BN_mul);
extern DLSYM_PROTOTYPE(BN_new);
+extern DLSYM_PROTOTYPE(BN_nnmod);
extern DLSYM_PROTOTYPE(BN_num_bits);
+extern DLSYM_PROTOTYPE(BN_secure_new);
+extern DLSYM_PROTOTYPE(BN_set_word);
+extern DLSYM_PROTOTYPE(BN_sub_word);
extern DLSYM_PROTOTYPE(CRYPTO_free);
-extern DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING);
-extern DLSYM_PROTOTYPE(d2i_ECPKParameters);
-extern DLSYM_PROTOTYPE(d2i_PKCS7);
-extern DLSYM_PROTOTYPE(d2i_PUBKEY);
-extern DLSYM_PROTOTYPE(d2i_X509);
+extern DLSYM_PROTOTYPE(ECDSA_SIG_free);
extern DLSYM_PROTOTYPE(EC_GROUP_free);
-extern DLSYM_PROTOTYPE(EC_GROUP_get_curve_name);
-extern DLSYM_PROTOTYPE(EC_GROUP_get_curve);
-extern DLSYM_PROTOTYPE(EC_GROUP_get_field_type);
extern DLSYM_PROTOTYPE(EC_GROUP_get0_generator);
extern DLSYM_PROTOTYPE(EC_GROUP_get0_order);
+extern DLSYM_PROTOTYPE(EC_GROUP_get_curve);
+extern DLSYM_PROTOTYPE(EC_GROUP_get_curve_name);
+extern DLSYM_PROTOTYPE(EC_GROUP_get_field_type);
extern DLSYM_PROTOTYPE(EC_GROUP_new_by_curve_name);
extern DLSYM_PROTOTYPE(EC_POINT_free);
extern DLSYM_PROTOTYPE(EC_POINT_new);
extern DLSYM_PROTOTYPE(EC_POINT_oct2point);
extern DLSYM_PROTOTYPE(EC_POINT_point2oct);
-extern DLSYM_PROTOTYPE(ECDSA_SIG_free);
extern DLSYM_PROTOTYPE(ERR_clear_error);
-extern DLSYM_PROTOTYPE(ERR_error_string_n);
extern DLSYM_PROTOTYPE(ERR_error_string);
+extern DLSYM_PROTOTYPE(ERR_error_string_n);
extern DLSYM_PROTOTYPE(ERR_get_error);
-extern DLSYM_PROTOTYPE(EVP_aes_256_ctr);
-extern DLSYM_PROTOTYPE(EVP_aes_256_gcm);
extern DLSYM_PROTOTYPE(EVP_CIPHER_CTX_ctrl);
extern DLSYM_PROTOTYPE(EVP_CIPHER_CTX_free);
extern DLSYM_PROTOTYPE(EVP_CIPHER_CTX_new);
@@ -141,46 +156,48 @@ extern DLSYM_PROTOTYPE(EVP_DigestVerifyInit);
extern DLSYM_PROTOTYPE(EVP_EncryptFinal_ex);
extern DLSYM_PROTOTYPE(EVP_EncryptInit_ex);
extern DLSYM_PROTOTYPE(EVP_EncryptUpdate);
-extern DLSYM_PROTOTYPE(EVP_get_cipherbyname);
-extern DLSYM_PROTOTYPE(EVP_get_digestbyname);
extern DLSYM_PROTOTYPE(EVP_MAC_CTX_free);
+extern DLSYM_PROTOTYPE(EVP_MAC_CTX_new);
+extern DLSYM_PROTOTYPE(EVP_MAC_fetch);
+extern DLSYM_PROTOTYPE(EVP_MAC_final);
extern DLSYM_PROTOTYPE(EVP_MAC_free);
+extern DLSYM_PROTOTYPE(EVP_MAC_init);
+extern DLSYM_PROTOTYPE(EVP_MAC_update);
+extern DLSYM_PROTOTYPE(EVP_MD_CTX_copy_ex);
extern DLSYM_PROTOTYPE(EVP_MD_CTX_free);
extern DLSYM_PROTOTYPE(EVP_MD_CTX_get0_md);
extern DLSYM_PROTOTYPE(EVP_MD_CTX_new);
extern DLSYM_PROTOTYPE(EVP_MD_CTX_set_pkey_ctx);
extern DLSYM_PROTOTYPE(EVP_MD_free);
-extern DLSYM_PROTOTYPE(EVP_MD_get_size);
extern DLSYM_PROTOTYPE(EVP_MD_get0_name);
+extern DLSYM_PROTOTYPE(EVP_MD_get_size);
extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_free);
+extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new);
extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_from_name);
extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_id);
-extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new);
extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_rsa_padding);
extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_signature_md);
extern DLSYM_PROTOTYPE(EVP_PKEY_eq);
extern DLSYM_PROTOTYPE(EVP_PKEY_free);
-extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init);
extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata);
+extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init);
extern DLSYM_PROTOTYPE(EVP_PKEY_get_base_id);
extern DLSYM_PROTOTYPE(EVP_PKEY_get_id);
-extern DLSYM_PROTOTYPE(EVP_PKEY_keygen_init);
extern DLSYM_PROTOTYPE(EVP_PKEY_keygen);
-extern DLSYM_PROTOTYPE(EVP_PKEY_new_raw_public_key);
+extern DLSYM_PROTOTYPE(EVP_PKEY_keygen_init);
extern DLSYM_PROTOTYPE(EVP_PKEY_new);
-extern DLSYM_PROTOTYPE(EVP_PKEY_verify_init);
+extern DLSYM_PROTOTYPE(EVP_PKEY_new_raw_public_key);
extern DLSYM_PROTOTYPE(EVP_PKEY_verify);
+extern DLSYM_PROTOTYPE(EVP_PKEY_verify_init);
+extern DLSYM_PROTOTYPE(EVP_aes_256_ctr);
+extern DLSYM_PROTOTYPE(EVP_aes_256_gcm);
+extern DLSYM_PROTOTYPE(EVP_get_cipherbyname);
+extern DLSYM_PROTOTYPE(EVP_get_digestbyname);
extern DLSYM_PROTOTYPE(EVP_sha1);
extern DLSYM_PROTOTYPE(EVP_sha256);
extern DLSYM_PROTOTYPE(EVP_sha384);
extern DLSYM_PROTOTYPE(EVP_sha512);
extern DLSYM_PROTOTYPE(HMAC);
-extern DLSYM_PROTOTYPE(i2d_ASN1_INTEGER);
-extern DLSYM_PROTOTYPE(i2d_PKCS7_fp);
-extern DLSYM_PROTOTYPE(i2d_PKCS7);
-extern DLSYM_PROTOTYPE(i2d_PUBKEY);
-extern DLSYM_PROTOTYPE(i2d_X509_NAME);
-extern DLSYM_PROTOTYPE(i2d_X509);
extern DLSYM_PROTOTYPE(OBJ_nid2obj);
extern DLSYM_PROTOTYPE(OBJ_nid2sn);
extern DLSYM_PROTOTYPE(OBJ_sn2nid);
@@ -191,24 +208,29 @@ extern DLSYM_PROTOTYPE(OPENSSL_sk_pop_free);
extern DLSYM_PROTOTYPE(OPENSSL_sk_push);
extern DLSYM_PROTOTYPE(OPENSSL_sk_value);
extern DLSYM_PROTOTYPE(OSSL_EC_curve_nid2name);
+extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free);
+extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new);
+extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string);
+extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param);
extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_BN);
extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_end);
extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_octet_string);
extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_utf8_string);
extern DLSYM_PROTOTYPE(OSSL_PARAM_free);
-extern DLSYM_PROTOTYPE(PEM_read_PrivateKey);
extern DLSYM_PROTOTYPE(PEM_read_PUBKEY);
+extern DLSYM_PROTOTYPE(PEM_read_PrivateKey);
extern DLSYM_PROTOTYPE(PEM_read_X509);
-extern DLSYM_PROTOTYPE(PEM_write_PrivateKey);
extern DLSYM_PROTOTYPE(PEM_write_PUBKEY);
+extern DLSYM_PROTOTYPE(PEM_write_PrivateKey);
extern DLSYM_PROTOTYPE(PEM_write_X509);
extern DLSYM_PROTOTYPE(PKCS5_PBKDF2_HMAC);
+extern DLSYM_PROTOTYPE(PKCS7_ATTR_SIGN_it);
+extern DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free);
+extern DLSYM_PROTOTYPE(PKCS7_add0_attrib_signing_time);
+extern DLSYM_PROTOTYPE(PKCS7_add1_attrib_digest);
extern DLSYM_PROTOTYPE(PKCS7_add_attrib_content_type);
extern DLSYM_PROTOTYPE(PKCS7_add_attrib_smimecap);
extern DLSYM_PROTOTYPE(PKCS7_add_signed_attribute);
-extern DLSYM_PROTOTYPE(PKCS7_add0_attrib_signing_time);
-extern DLSYM_PROTOTYPE(PKCS7_add1_attrib_digest);
-extern DLSYM_PROTOTYPE(PKCS7_ATTR_SIGN_it);
extern DLSYM_PROTOTYPE(PKCS7_content_new);
extern DLSYM_PROTOTYPE(PKCS7_ctrl);
extern DLSYM_PROTOTYPE(PKCS7_dataFinal);
@@ -218,21 +240,31 @@ extern DLSYM_PROTOTYPE(PKCS7_get_signer_info);
extern DLSYM_PROTOTYPE(PKCS7_new);
extern DLSYM_PROTOTYPE(PKCS7_set_content);
extern DLSYM_PROTOTYPE(PKCS7_sign);
-extern DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free);
extern DLSYM_PROTOTYPE(PKCS7_verify);
extern DLSYM_PROTOTYPE(SHA1);
extern DLSYM_PROTOTYPE(SHA512);
extern DLSYM_PROTOTYPE(X509_ALGOR_free);
extern DLSYM_PROTOTYPE(X509_ATTRIBUTE_free);
+extern DLSYM_PROTOTYPE(X509_NAME_free);
+extern DLSYM_PROTOTYPE(X509_NAME_oneline);
+extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_host);
+extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_ip);
+extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set_hostflags);
extern DLSYM_PROTOTYPE(X509_free);
extern DLSYM_PROTOTYPE(X509_get_pubkey);
extern DLSYM_PROTOTYPE(X509_get_subject_name);
extern DLSYM_PROTOTYPE(X509_gmtime_adj);
-extern DLSYM_PROTOTYPE(X509_NAME_free);
-extern DLSYM_PROTOTYPE(X509_NAME_oneline);
-extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set_hostflags);
-extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_host);
-extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_ip);
+extern DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING);
+extern DLSYM_PROTOTYPE(d2i_ECPKParameters);
+extern DLSYM_PROTOTYPE(d2i_PKCS7);
+extern DLSYM_PROTOTYPE(d2i_PUBKEY);
+extern DLSYM_PROTOTYPE(d2i_X509);
+extern DLSYM_PROTOTYPE(i2d_ASN1_INTEGER);
+extern DLSYM_PROTOTYPE(i2d_PKCS7);
+extern DLSYM_PROTOTYPE(i2d_PKCS7_fp);
+extern DLSYM_PROTOTYPE(i2d_PUBKEY);
+extern DLSYM_PROTOTYPE(i2d_X509);
+extern DLSYM_PROTOTYPE(i2d_X509_NAME);
#if !defined(OPENSSL_NO_DEPRECATED_3_0)
DISABLE_WARNING_DEPRECATED_DECLARATIONS;
@@ -245,11 +277,11 @@ extern DLSYM_PROTOTYPE(EC_KEY_new);
extern DLSYM_PROTOTYPE(EC_KEY_set_group);
extern DLSYM_PROTOTYPE(EC_KEY_set_public_key);
extern DLSYM_PROTOTYPE(EVP_PKEY_assign);
+extern DLSYM_PROTOTYPE(RSAPublicKey_dup);
extern DLSYM_PROTOTYPE(RSA_free);
extern DLSYM_PROTOTYPE(RSA_new);
extern DLSYM_PROTOTYPE(RSA_set0_key);
extern DLSYM_PROTOTYPE(RSA_size);
-extern DLSYM_PROTOTYPE(RSAPublicKey_dup);
REENABLE_WARNING;
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EC_KEY*, sym_EC_KEY_free, EC_KEY_freep, NULL);
@@ -262,6 +294,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(RSA*, sym_RSA_free, RSA_freep, NULL);
#define sym_BIO_get_mem_ptr(b, pp) sym_BIO_ctrl((b), BIO_C_GET_BUF_MEM_PTR, 0, (char *) (pp))
#define sym_BIO_reset(b) sym_BIO_ctrl((b), BIO_CTRL_RESET, 0, NULL)
#define sym_BN_num_bytes(a) ((sym_BN_num_bits(a) + 7) / 8)
+#define sym_BN_one(a) sym_BN_set_word(a, 1)
#define sym_EVP_MD_CTX_get_size(ctx) sym_EVP_MD_get_size(sym_EVP_MD_CTX_get0_md(ctx))
#define sym_EVP_MD_CTX_get0_name(ctx) sym_EVP_MD_get0_name(sym_EVP_MD_CTX_get0_md(ctx))
#define sym_EVP_PKEY_assign_RSA(pkey, rsa) sym_EVP_PKEY_assign((pkey), EVP_PKEY_RSA, (rsa))
@@ -272,6 +305,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO_RENAME(void*, sym_OPENSSL_free, OPENSSL_f
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ASN1_OCTET_STRING*, sym_ASN1_OCTET_STRING_free, ASN1_OCTET_STRING_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ASN1_TIME*, sym_ASN1_TIME_free, ASN1_TIME_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIGNUM*, sym_BN_free, BN_freep, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIGNUM*, sym_BN_clear_free, BN_clear_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIO*, sym_BIO_free_all, BIO_free_allp, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIO*, sym_BIO_free, BIO_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BN_CTX*, sym_BN_CTX_free, BN_CTX_freep, NULL);
@@ -286,6 +320,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_MD_CTX*, sym_EVP_MD_CTX_free, EVP_MD
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_MD*, sym_EVP_MD_free, EVP_MD_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_PKEY_CTX*, sym_EVP_PKEY_CTX_free, EVP_PKEY_CTX_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_PKEY*, sym_EVP_PKEY_free, EVP_PKEY_freep, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_PARAM_BLD*, sym_OSSL_PARAM_BLD_free, OSSL_PARAM_BLD_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_PARAM*, sym_OSSL_PARAM_free, OSSL_PARAM_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(PKCS7_SIGNER_INFO*, sym_PKCS7_SIGNER_INFO_free, PKCS7_SIGNER_INFO_freep, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(PKCS7*, sym_PKCS7_free, PKCS7_freep, NULL);
diff --git a/src/shared/fsprg-openssl.c b/src/shared/fsprg-openssl.c
new file mode 100644
index 0000000000..167f0607f4
--- /dev/null
+++ b/src/shared/fsprg-openssl.c
@@ -0,0 +1,668 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/*
+ * This is based on
+ * fsprg v0.1 - (seekable) forward-secure pseudorandom generator
+ * Copyright © 2012 B. Poettering
+ * Contact: fsprg@point-at-infinity.org
+ *
+ * OpenSSL port of the original libgcrypt-based implementation.
+ *
+ * See "Practical Secure Logging: Seekable Sequential Key Generators"
+ * by G. A. Marson, B. Poettering for details:
+ *
+ * http://eprint.iacr.org/2013/397
+ */
+
+#include
+
+#include "alloc-util.h" /* IWYU pragma: keep */
+#include "crypto-util.h"
+#include "fsprg-openssl.h"
+#include "iovec-util.h"
+#include "logarithm.h"
+#include "sparse-endian.h"
+#include "unaligned.h"
+
+#define RND_GEN_P 0x01
+#define RND_GEN_Q 0x02
+#define RND_GEN_X 0x03
+
+/* Suppress a false positive from GCC's -Wstringop-overflow warning. Keep a local helper so the compiler can
+ * propagate the range check within this translation unit. */
+static bool secpar_is_valid(uint16_t secpar) {
+ return
+ secpar % 16 == 0 &&
+ secpar >= 16 &&
+ secpar <= 16384;
+}
+
+bool fsprg_secpar_is_valid(uint16_t secpar) {
+ return secpar_is_valid(secpar);
+}
+
+size_t fsprg_state_size(uint16_t secpar) {
+ assert(secpar_is_valid(secpar));
+
+ /* See comment in parse_state(). */
+ return sizeof(uint16_t) + 2 * secpar / 8 + sizeof(uint64_t);
+}
+
+#if HAVE_OPENSSL
+static int mpi_export(struct iovec *iov, const BIGNUM *x) {
+ assert(iovec_is_set(iov));
+ assert(x);
+
+ if (sym_BN_is_negative(x))
+ return -EINVAL;
+
+ if (sym_BN_num_bytes(x) > (int) iov->iov_len)
+ return -ENOSPC;
+
+ if (sym_BN_bn2binpad(x, iov->iov_base, iov->iov_len) != (int) iov->iov_len)
+ return -EIO;
+
+ return 0;
+}
+
+static int mpi_import(const struct iovec *iov, BIGNUM **ret) {
+ assert(iovec_is_set(iov));
+ assert(ret);
+
+ /* Allocate a new BIGNUM. */
+ _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new();
+ if (!x)
+ return -ENOMEM;
+
+ if (!sym_BN_bin2bn(iov->iov_base, iov->iov_len, x))
+ return -EIO;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+static int det_randomize(const struct iovec *seed, uint32_t idx, struct iovec *iov) {
+ assert(iovec_is_set(seed));
+ assert(iovec_is_valid(iov));
+
+ /* Expand (seed, idx) into a deterministic pseudorandom byte stream. */
+
+ if (!iovec_is_set(iov))
+ return 0;
+
+ _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = sym_EVP_MD_CTX_new();
+ if (!ctx)
+ return -ENOMEM;
+
+ if (sym_EVP_DigestInit_ex(ctx, sym_EVP_sha256(), NULL) <= 0)
+ return -EIO;
+
+ if (sym_EVP_DigestUpdate(ctx, seed->iov_base, seed->iov_len) <= 0)
+ return -EIO;
+
+ if (sym_EVP_DigestUpdate(ctx, (be32_t[]) { htobe32(idx) }, sizeof(be32_t)) <= 0)
+ return -EIO;
+
+ struct iovec v = *iov;
+ for (uint32_t ctr = 0; iovec_is_set(&v); ctr++) {
+ _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *tmp = sym_EVP_MD_CTX_new();
+ if (!tmp)
+ return -ENOMEM;
+
+ if (sym_EVP_MD_CTX_copy_ex(tmp, ctx) <= 0)
+ return -EIO;
+
+ if (sym_EVP_DigestUpdate(tmp, (be32_t[]) { htobe32(ctr) }, sizeof(be32_t)) <= 0)
+ return -EIO;
+
+ unsigned dlen;
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ CLEANUP_ERASE(digest);
+
+ if (sym_EVP_DigestFinal_ex(tmp, digest, &dlen) <= 0)
+ return -EIO;
+
+ size_t n = MIN(v.iov_len, dlen);
+ memcpy(v.iov_base, digest, n);
+ iovec_inc(&v, n);
+ }
+
+ return 0;
+}
+
+/* deterministically generate from seed/idx a prime of length (secpar / 2) bits that is 3 mod 4 */
+static int generate_prime3mod4(
+ uint16_t secpar,
+ const struct iovec *seed,
+ uint32_t idx,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(bn_ctx);
+ assert(ret);
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(iovec_erase) struct iovec iov = IOVEC_ALLOCA(secpar / 2 / 8);
+ r = det_randomize(seed, idx, &iov);
+ if (r < 0)
+ return r;
+
+ uint8_t *buf = iov.iov_base;
+
+ /* Set the upper two bits so that n = pq has the maximum size. */
+ buf[0] |= 0xc0;
+
+ /* Make the candidate congruent to 3 (mod 4). */
+ buf[iov.iov_len - 1] |= 0x03;
+
+ _cleanup_(BN_clear_freep) BIGNUM *p = NULL;
+ r = mpi_import(&iov, &p);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ /* negative: error, 0: composite, 1: prime */
+ r = sym_BN_check_prime(p, bn_ctx, /* cb= */ NULL);
+ if (r < 0)
+ return -EIO;
+ if (r > 0)
+ break;
+
+ /* Increment p by 4. */
+ if (sym_BN_add_word(p, 4) <= 0)
+ return -EIO;
+
+ /* Check if p is still (secpar / 2) bits. */
+ if (sym_BN_num_bits(p) != secpar / 2)
+ return -EOVERFLOW;
+ }
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
+
+static int generate_keys(
+ uint16_t secpar,
+ const struct iovec *seed,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret_p, /* prime */
+ BIGNUM **ret_q, /* prime */
+ BIGNUM **ret_n) { /* n = p * q */
+
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(bn_ctx);
+ assert(ret_p);
+ assert(ret_q);
+ assert(ret_n);
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(BN_clear_freep) BIGNUM *p = NULL;
+ r = generate_prime3mod4(secpar, seed, RND_GEN_P, bn_ctx, &p);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_clear_freep) BIGNUM *q = NULL;
+ r = generate_prime3mod4(secpar, seed, RND_GEN_Q, bn_ctx, &q);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_clear_freep) BIGNUM *n = sym_BN_secure_new();
+ if (!n)
+ return -ENOMEM;
+
+ if (sym_BN_mul(n, p, q, bn_ctx) <= 0)
+ return -EIO;
+
+ if (sym_BN_num_bits(n) != secpar)
+ return -EIO;
+
+ *ret_p = TAKE_PTR(p);
+ *ret_q = TAKE_PTR(q);
+ *ret_n = TAKE_PTR(n);
+ return 0;
+}
+
+/* deterministically generate from seed/idx a quadratic residue (mod n) */
+static int generate_square(
+ uint16_t secpar,
+ const struct iovec *seed,
+ uint32_t idx,
+ const BIGNUM *n,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(n);
+ assert(bn_ctx);
+ assert(ret);
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(iovec_erase) struct iovec iov = IOVEC_ALLOCA(secpar / 8);
+ r = det_randomize(seed, idx, &iov);
+ if (r < 0)
+ return r;
+
+ *(uint8_t*) iov.iov_base &= 0x7f; /* Clear the upper bit so that we are likely to have x < n. */
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = NULL;
+ r = mpi_import(&iov, &x);
+ if (r < 0)
+ return r;
+
+ /* x < n should always hold for a valid modulus generated by generate_keys(). */
+ if (sym_BN_cmp(x, n) >= 0)
+ return -EINVAL;
+
+ /* x := x^2 mod n */
+ if (sym_BN_mod_sqr(x, x, n, bn_ctx) <= 0)
+ return -EIO;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+/* Compute 2^m mod phi(p), where p is prime and phi(p) = p - 1. */
+static int compute_two_pow_mod_phi(
+ uint64_t m,
+ const BIGNUM *p,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ assert(p);
+ assert(bn_ctx);
+ assert(ret);
+
+ _cleanup_(BN_clear_freep) BIGNUM *phi = sym_BN_secure_new();
+ if (!phi)
+ return -ENOMEM;
+
+ /* phi := p */
+ if (!sym_BN_copy(phi, p))
+ return -EIO;
+
+ /* phi := p - 1 */
+ if (sym_BN_sub_word(phi, 1) <= 0)
+ return -EIO;
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new();
+ if (!x)
+ return -ENOMEM;
+
+ /* x := 1 */
+ if (!sym_BN_one(x))
+ return -EIO;
+
+ if (m == 0) {
+ /* 2^0 mod phi = 1. */
+ *ret = TAKE_PTR(x);
+ return 0;
+ }
+
+ /* Square-and-multiply. Iterate over the bits of m from MSB to LSB. */
+ for (int n = LOG2ULL(m); n >= 0; n--) {
+ if (sym_BN_mod_sqr(x, x, phi, bn_ctx) <= 0) /* x := x^2 mod phi */
+ return -EIO;
+
+ if (m & (UINT64_C(1) << n) &&
+ sym_BN_mod_lshift1_quick(x, x, phi) <= 0) /* x := 2x mod phi */
+ return -EIO;
+ }
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+/* Decompose x ∈ Z_n into (xp, xq) ∈ Z_p × Z_q using the Chinese Remainder Theorem. */
+static int crt_decompose(
+ const BIGNUM *x,
+ const BIGNUM *p,
+ const BIGNUM *q,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret_xp,
+ BIGNUM **ret_xq) {
+
+ assert(x);
+ assert(p);
+ assert(q);
+ assert(bn_ctx);
+ assert(ret_xp);
+ assert(ret_xq);
+
+ _cleanup_(BN_clear_freep) BIGNUM *xp = sym_BN_secure_new();
+ if (!xp)
+ return -ENOMEM;
+
+ _cleanup_(BN_clear_freep) BIGNUM *xq = sym_BN_secure_new();
+ if (!xq)
+ return -ENOMEM;
+
+ if (sym_BN_nnmod(xp, x, p, bn_ctx) <= 0)
+ return -EIO;
+
+ if (sym_BN_nnmod(xq, x, q, bn_ctx) <= 0)
+ return -EIO;
+
+ *ret_xp = TAKE_PTR(xp);
+ *ret_xq = TAKE_PTR(xq);
+ return 0;
+}
+
+/* Compose (xp, xq) ∈ Z_p × Z_q into x ∈ Z_n using the Chinese Remainder Theorem. */
+static int crt_compose(
+ const BIGNUM *xp,
+ const BIGNUM *xq,
+ const BIGNUM *p,
+ const BIGNUM *q,
+ BN_CTX *bn_ctx,
+ BIGNUM **ret) {
+
+ assert(xp);
+ assert(xq);
+ assert(p);
+ assert(q);
+ assert(bn_ctx);
+ assert(ret);
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new();
+ if (!x)
+ return -ENOMEM;
+
+ /* x := xq - xp mod q */
+ if (sym_BN_mod_sub(x, xq, xp, q, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Compute p^-1 mod q. */
+ _cleanup_(BN_clear_freep) BIGNUM *p_inv = sym_BN_secure_new();
+ if (!p_inv)
+ return -ENOMEM;
+
+ if (!sym_BN_mod_inverse(p_inv, p, q, bn_ctx))
+ return -EIO;
+
+ /* x := (xq - xp) * p^-1 mod q */
+ if (sym_BN_mod_mul(x, x, p_inv, q, bn_ctx) <= 0)
+ return -EIO;
+
+ /* x := p * ((xq - xp) * p^-1 mod q) */
+ if (sym_BN_mul(x, x, p, bn_ctx) <= 0)
+ return -EIO;
+
+ /* x := p * ((xq - xp) * p^-1 mod q) + xp */
+ if (sym_BN_add(x, x, xp) <= 0)
+ return -EIO;
+
+ *ret = TAKE_PTR(x);
+ return 0;
+}
+
+static int save_state(
+ uint16_t secpar,
+ const BIGNUM *modulus,
+ const BIGNUM *current,
+ uint64_t epoch,
+ struct iovec *state) {
+
+ int r;
+
+ assert(modulus);
+ assert(current);
+ assert(iovec_is_set(state));
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ if (state->iov_len != fsprg_state_size(secpar))
+ return -EINVAL;
+
+ uint8_t *s = state->iov_base;
+
+ /* header */
+ unaligned_write_be16(s, secpar / 16 - 1);
+ s += sizeof(uint16_t);
+
+ /* modulus */
+ r = mpi_export(&IOVEC_MAKE(s, secpar / 8), modulus);
+ if (r < 0)
+ return r;
+ s += secpar / 8;
+
+ /* current */
+ r = mpi_export(&IOVEC_MAKE(s, secpar / 8), current);
+ if (r < 0)
+ return r;
+ s += secpar / 8;
+
+ /* epoch */
+ unaligned_write_be64(s, epoch);
+ return 0;
+}
+
+static int parse_state(
+ const struct iovec *state,
+ uint16_t *ret_secpar,
+ BIGNUM **ret_modulus,
+ BIGNUM **ret_current,
+ uint64_t *ret_epoch) {
+
+ int r;
+
+ assert(iovec_is_set(state));
+
+ /* The serialized state consists of:
+ * - header: 2 bytes. Encodes secpar / 16 - 1, where 16 <= secpar <= 16384.
+ * - modulus (a.k.a. n): secpar / 8 bytes.
+ * - current (a.k.a. x): secpar / 8 bytes.
+ * - epoch: 8 bytes. */
+
+ if (state->iov_len < sizeof(uint16_t))
+ return -EBADMSG;
+
+ uint16_t header = unaligned_read_be16(state->iov_base);
+ if (header >= 1024)
+ return -EBADMSG;
+
+ uint16_t secpar = 16 * (header + 1);
+ if (state->iov_len != fsprg_state_size(secpar))
+ return -EBADMSG;
+
+ _cleanup_(BN_clear_freep) BIGNUM *modulus = NULL;
+ if (ret_modulus) {
+ r = mpi_import(&IOVEC_MAKE((uint8_t*) state->iov_base + sizeof(uint16_t), secpar / 8), &modulus);
+ if (r < 0)
+ return r;
+ }
+
+ _cleanup_(BN_clear_freep) BIGNUM *current = NULL;
+ if (ret_current) {
+ r = mpi_import(&IOVEC_MAKE((uint8_t*) state->iov_base + sizeof(uint16_t) + secpar / 8, secpar / 8), ¤t);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_secpar)
+ *ret_secpar = secpar;
+ if (ret_modulus)
+ *ret_modulus = TAKE_PTR(modulus);
+ if (ret_current)
+ *ret_current = TAKE_PTR(current);
+ if (ret_epoch)
+ *ret_epoch = unaligned_read_be64((uint8_t*) state->iov_base + sizeof(uint16_t) + 2 * secpar / 8);
+ return 0;
+}
+#endif
+
+int fsprg_generate_state(
+ uint16_t secpar,
+ uint64_t epoch,
+ const struct iovec *seed,
+ struct iovec *state) {
+
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(seed));
+ assert(iovec_is_set(state));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ if (!secpar_is_valid(secpar))
+ return -EINVAL;
+
+ _cleanup_(BN_CTX_freep) BN_CTX *bn_ctx = sym_BN_CTX_secure_new();
+ if (!bn_ctx)
+ return -ENOMEM;
+
+ _cleanup_(BN_clear_freep) BIGNUM *p = NULL, *q = NULL, *n = NULL;
+ r = generate_keys(secpar, seed, bn_ctx, &p, &q, &n);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_clear_freep) BIGNUM *x = NULL;
+ r = generate_square(secpar, seed, RND_GEN_X, n, bn_ctx, &x);
+ if (r < 0)
+ return r;
+
+ if (epoch != 0) {
+ /* Decompose x from Z_n into Z_p × Z_q using CRT. */
+ _cleanup_(BN_clear_freep) BIGNUM *xp = NULL, *xq = NULL;
+ r = crt_decompose(x, p, q, bn_ctx, &xp, &xq);
+ if (r < 0)
+ return r;
+
+ /* Compute 2^epoch (mod phi(p)). */
+ _cleanup_(BN_clear_freep) BIGNUM *kp = NULL;
+ r = compute_two_pow_mod_phi(epoch, p, bn_ctx, &kp);
+ if (r < 0)
+ return r;
+
+ /* Compute 2^epoch (mod phi(q)). */
+ _cleanup_(BN_clear_freep) BIGNUM *kq = NULL;
+ r = compute_two_pow_mod_phi(epoch, q, bn_ctx, &kq);
+ if (r < 0)
+ return r;
+
+ /* Compute x^(2^epoch) (mod p). */
+ if (sym_BN_mod_exp(xp, xp, kp, p, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Compute x^(2^epoch) (mod q). */
+ if (sym_BN_mod_exp(xq, xq, kq, q, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Reconstruct x modulo n from its residues modulo p and q. */
+ BN_clear_freep(&x);
+ r = crt_compose(xp, xq, p, q, bn_ctx, &x);
+ if (r < 0)
+ return r;
+ }
+
+ return save_state(secpar, n, x, epoch, state);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int fsprg_evolve(struct iovec *state) {
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(state));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ uint16_t secpar;
+ uint64_t epoch;
+ _cleanup_(BN_clear_freep) BIGNUM *n = NULL, *x = NULL;
+ r = parse_state(state, &secpar, &n, &x, &epoch);
+ if (r < 0)
+ return r;
+
+ _cleanup_(BN_CTX_freep) BN_CTX *bn_ctx = sym_BN_CTX_secure_new();
+ if (!bn_ctx)
+ return -ENOMEM;
+
+ /* Update the current state x := x^2 mod n */
+ if (sym_BN_mod_sqr(x, x, n, bn_ctx) <= 0)
+ return -EIO;
+
+ /* Store the updated current state. */
+ uint8_t *s = (uint8_t*) state->iov_base + sizeof(uint16_t) + secpar / 8;
+ r = mpi_export(&IOVEC_MAKE(s, secpar / 8), x);
+ if (r < 0)
+ return r;
+
+ /* Increment epoch */
+ unaligned_write_be64(s + secpar / 8, epoch + 1);
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int fsprg_get_epoch(const struct iovec *state, uint64_t *ret) {
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(state));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ return parse_state(
+ state,
+ /* ret_secpar= */ NULL,
+ /* ret_modulus= */ NULL,
+ /* ret_current= */ NULL,
+ ret);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+int fsprg_get_key(const struct iovec *state, struct iovec *key) {
+#if HAVE_OPENSSL
+ int r;
+
+ assert(iovec_is_set(state));
+ assert(iovec_is_valid(key));
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ uint16_t secpar;
+ r = parse_state(state,
+ /* ret_secpar= */ &secpar,
+ /* ret_modulus= */ NULL,
+ /* ret_current= */ NULL,
+ /* ret_epoch= */ NULL);
+ if (r < 0)
+ return r;
+
+ struct iovec seed = IOVEC_MAKE(
+ (uint8_t*) state->iov_base + sizeof(uint16_t),
+ 2 * secpar / 8 + sizeof(uint64_t));
+
+ return det_randomize(&seed, /* idx= */ 0, key);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
diff --git a/src/shared/fsprg-openssl.h b/src/shared/fsprg-openssl.h
new file mode 100644
index 0000000000..e9e5649617
--- /dev/null
+++ b/src/shared/fsprg-openssl.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-forward.h"
+
+#define FSPRG_RECOMMENDED_SECPAR 1536
+#define FSPRG_RECOMMENDED_SEEDLEN (96/8)
+
+bool fsprg_secpar_is_valid(uint16_t secpar);
+size_t fsprg_state_size(uint16_t secpar);
+int fsprg_generate_state(
+ uint16_t secpar,
+ uint64_t epoch,
+ const struct iovec *seed,
+ struct iovec *state);
+int fsprg_evolve(struct iovec *state);
+int fsprg_get_epoch(const struct iovec *state, uint64_t *ret);
+int fsprg_get_key(const struct iovec *state, struct iovec *key);
diff --git a/src/shared/journal-authenticate.c b/src/shared/journal-authenticate.c
new file mode 100644
index 0000000000..20434c9b0b
--- /dev/null
+++ b/src/shared/journal-authenticate.c
@@ -0,0 +1,596 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include
+#include
+#include
+
+#include "alloc-util.h"
+#include "crypto-util.h"
+#include "fd-util.h"
+#include "fsprg-openssl.h"
+#include "hexdecoct.h"
+#include "iovec-util.h"
+#include "journal-authenticate.h"
+#include "journal-def.h"
+#include "journal-file.h"
+#include "log.h"
+#include "memory-util.h"
+#include "string-util.h"
+#include "time-util.h"
+
+#if HAVE_OPENSSL
+
+struct JournalAuthContext {
+ EVP_MAC *hmac;
+ EVP_MAC_CTX *hmac_ctx;
+ OSSL_PARAM *ossl_params;
+ bool hmac_running;
+
+ FSSHeader *fss_file;
+ size_t fss_mmap_size;
+
+ uint64_t fss_start_usec;
+ uint64_t fss_interval_usec;
+
+ struct iovec fsprg_state;
+ struct iovec fsprg_seed;
+};
+
+static JournalAuthContext* journal_auth_free(JournalAuthContext *c) {
+ if (!c)
+ return NULL;
+
+ if (c->fss_file)
+ munmap(c->fss_file, c->fss_mmap_size);
+ else
+ iovec_done_erase(&c->fsprg_state);
+
+ iovec_done_erase(&c->fsprg_seed);
+
+ if (c->ossl_params)
+ sym_OSSL_PARAM_free(c->ossl_params);
+ if (c->hmac_ctx)
+ sym_EVP_MAC_CTX_free(c->hmac_ctx);
+ if (c->hmac)
+ sym_EVP_MAC_free(c->hmac);
+
+ return mfree(c);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JournalAuthContext*, journal_auth_free);
+
+static void* fssheader_free(FSSHeader *p) {
+ /* mmap() returns MAP_FAILED on error and sets the errno */
+ if (!p || p == MAP_FAILED)
+ return NULL;
+
+ assert_se(munmap(p, PAGE_ALIGN(sizeof(FSSHeader))) >= 0);
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader*, fssheader_free);
+
+static int journal_auth_load(JournalAuthContext **ret) {
+ int r;
+
+ assert(ret);
+
+ /* This function is used to determine whether sealing should be enabled in the journal header so we
+ * can't check the header to check if sealing is enabled here. */
+
+ sd_id128_t machine;
+ r = sd_id128_get_machine(&machine);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ char *path = NULL;
+ if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
+ SD_ID128_FORMAT_VAL(machine)) < 0)
+ return -ENOMEM;
+
+ _cleanup_close_ int fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ log_error_errno(errno, "Failed to open %s: %m", path);
+
+ return -errno;
+ }
+
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (st.st_size < (off_t) sizeof(FSSHeader))
+ return -ENODATA;
+
+ _cleanup_(fssheader_freep) FSSHeader *header =
+ mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
+ if (header == MAP_FAILED)
+ return -errno;
+
+ if (memcmp(header->signature, (const uint8_t[]) FSS_HEADER_SIGNATURE, 8) != 0)
+ return -EBADMSG;
+
+ if (header->incompatible_flags != 0)
+ return -EPROTONOSUPPORT;
+
+ if (le64toh(header->header_size) < sizeof(FSSHeader))
+ return -EBADMSG;
+
+ uint16_t secpar = le16toh(header->fsprg_secpar);
+ if (!fsprg_secpar_is_valid(secpar))
+ return -EBADMSG;
+
+ if (le64toh(header->fsprg_state_size) != fsprg_state_size(secpar))
+ return -EBADMSG;
+
+ uint64_t fss_file_size;
+ if (!ADD_SAFE(&fss_file_size, le64toh(header->header_size), le64toh(header->fsprg_state_size)))
+ return -EBADMSG;
+
+ if (fss_file_size >= SIZE_MAX)
+ return -EBADMSG;
+
+ if ((uint64_t) st.st_size < fss_file_size)
+ return -ENODATA;
+
+ if (!sd_id128_equal(machine, header->machine_id))
+ return -EHOSTDOWN;
+
+ if (le64toh(header->start_usec) <= 0 || le64toh(header->interval_usec) <= 0)
+ return -EBADMSG;
+
+ _cleanup_(journal_auth_freep) JournalAuthContext *c = new0(JournalAuthContext, 1);
+ if (!c)
+ return -ENOMEM;
+
+ size_t sz = PAGE_ALIGN(fss_file_size);
+ if (sz >= SIZE_MAX)
+ return -EBADMSG;
+
+ FSSHeader *p = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (p == MAP_FAILED)
+ return -errno;
+
+ *c = (JournalAuthContext) {
+ .fss_mmap_size = sz,
+ .fss_file = p,
+
+ .fss_start_usec = le64toh(p->start_usec),
+ .fss_interval_usec = le64toh(p->interval_usec),
+
+ .fsprg_state = IOVEC_MAKE(
+ (uint8_t*) p + le64toh(p->header_size),
+ le64toh(p->fsprg_state_size)),
+ };
+
+ *ret = TAKE_PTR(c);
+ return 0;
+}
+
+static int journal_auth_load_key(JournalAuthContext **ret, const char *key) {
+ int r;
+
+ assert(ret);
+ assert(key);
+
+ size_t seed_size = FSPRG_RECOMMENDED_SEEDLEN;
+ _cleanup_(erase_and_freep) uint8_t *seed = malloc(seed_size);
+ if (!seed)
+ return -ENOMEM;
+
+ const char *k = key;
+ for (size_t c = 0; c < seed_size; c++) {
+ int x, y;
+
+ k = skip_leading_chars(k, "-");
+
+ x = unhexchar(*k);
+ if (x < 0)
+ return -EKEYREJECTED;
+ k++;
+
+ y = unhexchar(*k);
+ if (y < 0)
+ return -EKEYREJECTED;
+ k++;
+
+ seed[c] = (uint8_t) (x * 16 + y);
+ }
+
+ if (*k != '/')
+ return -EKEYREJECTED;
+ k++;
+
+ uint64_t start, interval;
+ r = sscanf(k, "%"PRIx64"-%"PRIx64, &start, &interval);
+ if (r != 2)
+ return -EKEYREJECTED;
+
+ if (start == 0 || interval == 0)
+ return -EKEYREJECTED;
+
+ uint64_t start_usec;
+ if (!MUL_SAFE(&start_usec, start, interval))
+ return -EKEYREJECTED;
+
+ _cleanup_(journal_auth_freep) JournalAuthContext *c = new(JournalAuthContext, 1);
+ if (!c)
+ return -ENOMEM;
+
+ *c = (JournalAuthContext) {
+ .fss_start_usec = start_usec,
+ .fss_interval_usec = interval,
+
+ .fsprg_seed = IOVEC_MAKE(TAKE_PTR(seed), seed_size),
+ };
+
+ *ret = TAKE_PTR(c);
+ return 0;
+}
+
+static int journal_auth_epoch_to_realtime_usec(const JournalAuthContext *c, uint64_t epoch, usec_t *ret_start, usec_t *ret_end) {
+ assert(c);
+ assert(c->fss_start_usec > 0);
+ assert(c->fss_interval_usec > 0);
+
+ uint64_t start, end;
+ if (!MUL_SAFE(&start, epoch, c->fss_interval_usec) ||
+ !INC_SAFE(&start, c->fss_start_usec) ||
+ !ADD_SAFE(&end, start, c->fss_interval_usec))
+ return -ERANGE;
+
+ if (ret_start)
+ *ret_start = start;
+ if (ret_end)
+ *ret_end = end;
+
+ return 0;
+}
+
+static int journal_auth_next_evolve_usec(const JournalAuthContext *c, usec_t *ret) {
+ int r;
+
+ assert(c);
+
+ uint64_t epoch;
+ r = fsprg_get_epoch(&c->fsprg_state, &epoch);
+ if (r < 0)
+ return r;
+
+ return journal_auth_epoch_to_realtime_usec(c, epoch, /* ret_start= */ NULL, ret);
+}
+
+static int journal_auth_seek(JournalAuthContext *c, uint64_t goal) {
+ int r;
+
+ assert(c);
+ assert(iovec_is_set(&c->fsprg_seed));
+
+ if (iovec_is_set(&c->fsprg_state)) {
+ uint64_t epoch;
+ r = fsprg_get_epoch(&c->fsprg_state, &epoch);
+ if (r < 0)
+ return r;
+ if (goal == epoch)
+ return 0;
+
+ if (goal == epoch + 1)
+ return fsprg_evolve(&c->fsprg_state);
+ } else {
+ r = iovec_alloc(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR), &c->fsprg_state);
+ if (r < 0)
+ return r;
+ }
+
+ log_debug("Seeking FSPRG key to %"PRIu64".", goal);
+ return fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, goal, &c->fsprg_seed, &c->fsprg_state);
+}
+
+static int journal_auth_setup(JournalAuthContext *c) {
+ int r;
+
+ assert(c);
+
+ if (c->hmac)
+ return 0;
+
+ r = dlopen_libcrypto(LOG_DEBUG);
+ if (r < 0)
+ return r;
+
+ _cleanup_(EVP_MAC_freep) EVP_MAC *hmac = sym_EVP_MAC_fetch(NULL, "HMAC", NULL);
+ if (!hmac)
+ return log_openssl_errors(LOG_DEBUG, "EVP_MAC_fetch() failed");
+
+ _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *ctx = sym_EVP_MAC_CTX_new(hmac);
+ if (!ctx)
+ return log_openssl_errors(LOG_DEBUG, "EVP_MAC_CTX_new() failed");
+
+ _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = sym_OSSL_PARAM_BLD_new();
+ if (!bld)
+ return log_openssl_errors(LOG_DEBUG, "OSSL_PARAM_BLD_new() failed");
+
+ if (sym_OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, "SHA256", 0) <= 0)
+ return log_openssl_errors(LOG_DEBUG, "OSSL_PARAM_BLD_push_utf8_string() failed");
+
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = sym_OSSL_PARAM_BLD_to_param(bld);
+ if (!params)
+ return log_openssl_errors(LOG_DEBUG, "OSSL_PARAM_BLD_to_param() failed");
+
+ c->hmac = TAKE_PTR(hmac);
+ c->hmac_ctx = TAKE_PTR(ctx);
+ c->ossl_params = TAKE_PTR(params);
+ return 0;
+}
+
+static int journal_auth_start(JournalAuthContext *c) {
+ int r;
+
+ assert(c);
+
+ if (c->hmac_running)
+ return 0;
+
+ r = journal_auth_setup(c);
+ if (r < 0)
+ return r;
+
+ uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
+ CLEANUP_ERASE(key);
+ r = fsprg_get_key(&c->fsprg_state, &IOVEC_MAKE(key, sizeof(key)));
+ if (r < 0)
+ return r;
+
+ /* Prepare HMAC for next cycle */
+ if (sym_EVP_MAC_init(c->hmac_ctx, key, sizeof(key), c->ossl_params) <= 0)
+ return log_openssl_errors(LOG_DEBUG, "sym_EVP_MAC_init() failed");
+
+ c->hmac_running = true;
+ return 0;
+}
+
+static int journal_auth_end(JournalAuthContext *c, uint8_t ret[static TAG_LENGTH]) {
+ assert(c);
+ assert(ret);
+
+ if (!c->hmac_running)
+ return -EINVAL;
+
+ c->hmac_running = false;
+
+ uint8_t tag[TAG_LENGTH];
+ CLEANUP_ERASE(tag);
+
+ size_t len;
+ if (sym_EVP_MAC_final(c->hmac_ctx, tag, &len, TAG_LENGTH) <= 0 || len != TAG_LENGTH)
+ return -EIO;
+
+ memcpy(ret, tag, TAG_LENGTH);
+ return 0;
+}
+
+static int journal_auth_put_header(JournalAuthContext *c, JournalFile *f) {
+ int r;
+
+ assert(c);
+ assert(f);
+
+ r = journal_auth_start(c);
+ if (r < 0)
+ return r;
+
+ /* All but state+reserved, boot_id, arena_size,
+ * tail_object_offset, n_objects, n_entries,
+ * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
+ * head_entry_realtime, tail_entry_realtime,
+ * tail_entry_monotonic, n_data, n_fields, n_tags,
+ * n_entry_arrays. */
+
+ if (sym_EVP_MAC_update(c->hmac_ctx, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)) <= 0)
+ return -EIO;
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id)) <= 0)
+ return -EIO;
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)) <= 0)
+ return -EIO;
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)) <= 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int journal_auth_put_object(JournalAuthContext *c, JournalFile *f, ObjectType type, Object *o, uint64_t p) {
+ int r;
+
+ assert(c);
+ assert(f);
+
+ r = journal_auth_start(c);
+ if (r < 0)
+ return r;
+
+ if (!o) {
+ r = journal_file_move_to_object(f, type, p, &o);
+ if (r < 0)
+ return r;
+ } else if (type > OBJECT_UNUSED && o->object.type != type)
+ return -EBADMSG;
+
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) o, offsetof(ObjectHeader, payload)) <= 0)
+ return -EIO;
+
+ switch (o->object.type) {
+
+ case OBJECT_DATA:
+ /* All but hash and payload are mutable */
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->data.hash, sizeof(o->data.hash)) <= 0)
+ return -EIO;
+ if (sym_EVP_MAC_update(c->hmac_ctx, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f)) <= 0)
+ return -EIO;
+ break;
+
+ case OBJECT_FIELD:
+ /* Same here */
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->field.hash, sizeof(o->field.hash)) <= 0)
+ return -EIO;
+ if (sym_EVP_MAC_update(c->hmac_ctx, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)) <= 0)
+ return -EIO;
+ break;
+
+ case OBJECT_ENTRY:
+ /* All */
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum)) <= 0)
+ return -EIO;
+ break;
+
+ case OBJECT_FIELD_HASH_TABLE:
+ case OBJECT_DATA_HASH_TABLE:
+ case OBJECT_ENTRY_ARRAY:
+ /* Nothing: everything is mutable */
+ break;
+
+ case OBJECT_TAG:
+ /* All but the tag itself */
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->tag.seqnum, sizeof(o->tag.seqnum)) <= 0)
+ return -EIO;
+ if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->tag.epoch, sizeof(o->tag.epoch)) <= 0)
+ return -EIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int journal_auth_append_tag(JournalAuthContext *c, JournalFile *f) {
+ int r;
+
+ assert(c);
+ assert(f);
+
+ r = journal_auth_start(c);
+ if (r < 0)
+ return r;
+
+ Object *o;
+ uint64_t p;
+ r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
+ if (r < 0)
+ return r;
+
+ uint64_t seqnum = le64toh(f->header->n_tags) + 1;
+ f->header->n_tags = htole64(seqnum);
+
+ o->tag.seqnum = htole64(seqnum);
+
+ uint64_t epoch;
+ r = fsprg_get_epoch(&c->fsprg_state, &epoch);
+ if (r < 0)
+ return r;
+ o->tag.epoch = htole64(epoch);
+
+ log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
+ le64toh(o->tag.seqnum), epoch);
+
+ /* Add the tag object itself, so that we can protect its
+ * header. This will exclude the actual hash value in it */
+ r = journal_auth_put_object(c, f, OBJECT_TAG, o, p);
+ if (r < 0)
+ return r;
+
+ /* Get the HMAC tag and store it in the object */
+ return journal_auth_end(c, o->tag.tag);
+}
+
+static int journal_auth_append_tag_first(JournalAuthContext *c, JournalFile *f) {
+ uint64_t p;
+ int r;
+
+ assert(c);
+ assert(f);
+
+ log_debug("Calculating first tag...");
+
+ r = journal_auth_put_header(c, f);
+ if (r < 0)
+ return r;
+
+ p = le64toh(f->header->field_hash_table_offset);
+ if (p < offsetof(Object, hash_table.items))
+ return -EINVAL;
+ p -= offsetof(Object, hash_table.items);
+
+ r = journal_auth_put_object(c, f, OBJECT_FIELD_HASH_TABLE, NULL, p);
+ if (r < 0)
+ return r;
+
+ p = le64toh(f->header->data_hash_table_offset);
+ if (p < offsetof(Object, hash_table.items))
+ return -EINVAL;
+ p -= offsetof(Object, hash_table.items);
+
+ r = journal_auth_put_object(c, f, OBJECT_DATA_HASH_TABLE, NULL, p);
+ if (r < 0)
+ return r;
+
+ return journal_auth_append_tag(c, f);
+}
+
+static int journal_auth_append_tag_maybe(JournalAuthContext *c, JournalFile *f, usec_t realtime) {
+ int r;
+
+ assert(c);
+ assert(c->fss_start_usec > 0);
+ assert(c->fss_interval_usec > 0);
+ assert(f);
+
+ if (realtime <= 0)
+ realtime = now(CLOCK_REALTIME);
+
+ uint64_t goal = usec_sub_unsigned(realtime, c->fss_start_usec) / c->fss_interval_usec;
+
+ for (;;) {
+ uint64_t epoch;
+ r = fsprg_get_epoch(&c->fsprg_state, &epoch);
+ if (r < 0)
+ return r;
+ if (epoch >= goal)
+ return 0;
+
+ r = journal_auth_append_tag(c, f);
+ if (r < 0)
+ return r;
+
+ r = fsprg_evolve(&c->fsprg_state);
+ if (r < 0)
+ return r;
+ }
+}
+
+static const JournalAuthOps journal_auth_ops = {
+ .free = journal_auth_free,
+ .load = journal_auth_load,
+ .load_key = journal_auth_load_key,
+ .epoch_to_realtime_usec = journal_auth_epoch_to_realtime_usec,
+ .next_evolve_usec = journal_auth_next_evolve_usec,
+ .seek = journal_auth_seek,
+ .start = journal_auth_start,
+ .end = journal_auth_end,
+ .put_header = journal_auth_put_header,
+ .put_object = journal_auth_put_object,
+ .append_tag = journal_auth_append_tag,
+ .append_tag_first = journal_auth_append_tag_first,
+ .append_tag_maybe = journal_auth_append_tag_maybe,
+};
+
+void journal_auth_init(void) {
+ journal_auth_set_ops(&journal_auth_ops);
+}
+
+#else
+
+void journal_auth_init(void) {
+}
+
+#endif /* HAVE_OPENSSL */
diff --git a/src/shared/journal-authenticate.h b/src/shared/journal-authenticate.h
new file mode 100644
index 0000000000..a12afb9ac7
--- /dev/null
+++ b/src/shared/journal-authenticate.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "journal-authenticate-internal.h" /* IWYU pragma: export */
+#include "shared-forward.h"
+
+void journal_auth_init(void);
diff --git a/src/shared/journal-file-util.c b/src/shared/journal-file-util.c
index 8aecda9610..1567defac5 100644
--- a/src/shared/journal-file-util.c
+++ b/src/shared/journal-file-util.c
@@ -405,23 +405,15 @@ bool journal_file_is_offlining(JournalFile *f) {
return true;
}
-void journal_file_write_final_tag(JournalFile *f) {
- assert(f);
-#if HAVE_GCRYPT
- if (!JOURNAL_HEADER_SEALED(f->header) || !journal_file_writable(f))
- return;
-
- int r = journal_file_append_tag(f);
- if (r < 0)
- log_debug_errno(r, "Failed to append tag when closing journal: %m");
-#endif
-}
-
JournalFile* journal_file_offline_close(JournalFile *f) {
+ int r;
+
if (!f)
return NULL;
- journal_file_write_final_tag(f);
+ r = journal_file_auth_append_tag(f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to append tag when closing journal, ignoring: %m");
if (sd_event_source_get_enabled(f->post_change_timer, NULL) > 0)
journal_file_post_change(f);
@@ -464,7 +456,10 @@ int journal_file_rotate(
assert(f);
assert(*f);
- journal_file_write_final_tag(*f);
+ r = journal_file_auth_append_tag(*f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to append tag when closing journal, ignoring: %m");
+
r = journal_file_archive(*f, &path);
if (r < 0)
return r;
diff --git a/src/shared/journal-file-util.h b/src/shared/journal-file-util.h
index d3ffe4e7a6..a3a8781218 100644
--- a/src/shared/journal-file-util.h
+++ b/src/shared/journal-file-util.h
@@ -6,7 +6,6 @@
int journal_file_set_offline(JournalFile *f, bool wait);
bool journal_file_is_offlining(JournalFile *f);
-void journal_file_write_final_tag(JournalFile *f);
JournalFile* journal_file_offline_close(JournalFile *f);
DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_offline_close);
diff --git a/src/shared/meson.build b/src/shared/meson.build
index def42815c7..1b1ab668b2 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -87,6 +87,7 @@ shared_sources = files(
'firewall-util.c',
'fork-notify.c',
'format-table.c',
+ 'fsprg-openssl.c',
'fstab-util.c',
'generator.c',
'geneve-util.c',
@@ -110,6 +111,7 @@ shared_sources = files(
'ioprio-util.c',
'ip-protocol-list.c',
'ipvlan-util.c',
+ 'journal-authenticate.c',
'journal-file-util.c',
'journal-importer.c',
'journal-util.c',
@@ -405,7 +407,6 @@ libshared_deps = [libacl_cflags,
libelf_cflags,
libfdisk_cflags,
libfido2_cflags,
- libgcrypt_cflags,
libgnutls_cflags,
libidn2_cflags,
libkmod_cflags,
diff --git a/src/libsystemd/sd-journal/fsprg.c b/src/test/fsprg.c
similarity index 100%
rename from src/libsystemd/sd-journal/fsprg.c
rename to src/test/fsprg.c
diff --git a/src/libsystemd/sd-journal/fsprg.h b/src/test/fsprg.h
similarity index 100%
rename from src/libsystemd/sd-journal/fsprg.h
rename to src/test/fsprg.h
diff --git a/src/basic/gcrypt-util.c b/src/test/gcrypt-util.c
similarity index 96%
rename from src/basic/gcrypt-util.c
rename to src/test/gcrypt-util.c
index 824fc6c415..6197ddb9c2 100644
--- a/src/basic/gcrypt-util.c
+++ b/src/test/gcrypt-util.c
@@ -40,7 +40,6 @@ DLSYM_PROTOTYPE(gcry_mpi_subm) = NULL;
DLSYM_PROTOTYPE(gcry_mpi_sub_ui) = NULL;
DLSYM_PROTOTYPE(gcry_prime_check) = NULL;
DLSYM_PROTOTYPE(gcry_randomize) = NULL;
-DLSYM_PROTOTYPE(gcry_strerror) = NULL;
#endif
int dlopen_gcrypt(int log_level) {
@@ -82,8 +81,7 @@ int dlopen_gcrypt(int log_level) {
DLSYM_ARG(gcry_mpi_subm),
DLSYM_ARG(gcry_mpi_sub_ui),
DLSYM_ARG(gcry_prime_check),
- DLSYM_ARG(gcry_randomize),
- DLSYM_ARG(gcry_strerror));
+ DLSYM_ARG(gcry_randomize));
#else
return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
"gcrypt support is not compiled in.");
diff --git a/src/basic/gcrypt-util.h b/src/test/gcrypt-util.h
similarity index 94%
rename from src/basic/gcrypt-util.h
rename to src/test/gcrypt-util.h
index 2a85fa7e1d..0340488697 100644
--- a/src/basic/gcrypt-util.h
+++ b/src/test/gcrypt-util.h
@@ -56,7 +56,6 @@ extern DLSYM_PROTOTYPE(gcry_mpi_subm);
extern DLSYM_PROTOTYPE(gcry_mpi_sub_ui);
extern DLSYM_PROTOTYPE(gcry_prime_check);
extern DLSYM_PROTOTYPE(gcry_randomize);
-extern DLSYM_PROTOTYPE(gcry_strerror);
/* Copied from gcry_md_putc from gcrypt.h due to the need to call the sym_ variant */
#define sym_gcry_md_putc(h,c) \
@@ -66,14 +65,8 @@ extern DLSYM_PROTOTYPE(gcry_strerror);
sym_gcry_md_write((h__), NULL, 0); \
(h__)->buf[(h__)->bufpos++] = (c) & 0xff; \
} while(false)
-#else
-#define DLOPEN_GCRYPT(log_level, priority) dlopen_gcrypt(log_level)
-
-typedef struct gcry_md_handle *gcry_md_hd_t;
-
-static inline void sym_gcry_md_close(gcry_md_hd_t h) {
- assert(h == NULL);
-}
-#endif
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, sym_gcry_md_close, NULL);
+#else
+#define DLOPEN_GCRYPT(log_level, priority) dlopen_gcrypt(log_level)
+#endif
diff --git a/src/test/meson.build b/src/test/meson.build
index cd54f4d8dc..9d5a79c78e 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -300,10 +300,14 @@ executables += [
'type' : 'manual',
},
test_template + {
- 'sources' : files('test-dlopen-so.c'),
+ 'sources' : files(
+ 'test-dlopen-so.c',
+ 'gcrypt-util.c',
+ ),
'dependencies' : [
libblkid_cflags,
libfdisk_cflags,
+ libgcrypt_cflags,
libgnutls_cflags,
libkmod_cflags,
libmicrohttpd_cflags,
@@ -336,6 +340,18 @@ executables += [
'link_with' : libsystemd,
'type' : 'manual',
},
+ test_template + {
+ 'sources' : files(
+ 'test-fsprg.c',
+ 'fsprg.c',
+ 'gcrypt-util.c',
+ ),
+ 'dependencies' : [
+ libgcrypt_cflags,
+ libopenssl_cflags,
+ ],
+ 'conditions' : ['HAVE_OPENSSL'],
+ },
test_template + {
'sources' : files(
'test-hashmap.c',
diff --git a/src/test/test-fsprg.c b/src/test/test-fsprg.c
new file mode 100644
index 0000000000..94183e2246
--- /dev/null
+++ b/src/test/test-fsprg.c
@@ -0,0 +1,511 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#if HAVE_VALGRIND_VALGRIND_H
+# include
+#endif
+
+#include "crypto-util.h"
+#include "fsprg.h"
+#include "fsprg-openssl.h"
+#include "gcrypt-util.h"
+#include "iovec-util.h"
+#include "journal-def.h"
+#include "stdio-util.h"
+#include "tests.h"
+
+#define STATE_LENGTH (2 + 2 * FSPRG_RECOMMENDED_SECPAR / 8 + 8)
+#define KEY_LENGTH 32
+
+static const uint8_t expected_state[4][STATE_LENGTH] = {
+ {
+ 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15,
+ 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37,
+ 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88,
+ 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c,
+ 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b,
+ 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e,
+ 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86,
+ 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7,
+ 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71,
+ 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23,
+ 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9,
+ 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf,
+ 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71,
+ 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15,
+ 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a,
+ 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31,
+ 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f,
+ 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b,
+ 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b,
+ 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9,
+ 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0,
+ 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb,
+ 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9,
+ 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2,
+ 0x8c, 0x9d, 0xc9, 0xf8, 0x35, 0xe1, 0xec, 0x25,
+ 0xd5, 0xca, 0x98, 0x7a, 0x4f, 0xbd, 0x45, 0x50,
+ 0xbf, 0xff, 0x1c, 0xd8, 0x42, 0x95, 0xcb, 0xc5,
+ 0xa7, 0x0f, 0x75, 0xda, 0x6d, 0x5b, 0xa2, 0xeb,
+ 0x5b, 0x26, 0x9e, 0xe8, 0xa3, 0x23, 0x04, 0x4b,
+ 0x5b, 0x26, 0xb7, 0xe4, 0x7f, 0xcd, 0x82, 0x66,
+ 0x23, 0x7d, 0xf8, 0x57, 0x2c, 0x22, 0x0b, 0x7e,
+ 0x87, 0x3f, 0xe1, 0xc1, 0x1a, 0xea, 0xc3, 0x42,
+ 0x40, 0x03, 0xaf, 0x11, 0xd5, 0x07, 0xa1, 0x79,
+ 0x55, 0x34, 0x4a, 0x41, 0x6a, 0x75, 0x68, 0x13,
+ 0x37, 0xfd, 0x38, 0x97, 0x52, 0x4f, 0xbb, 0x68,
+ 0xb4, 0x9f, 0xa8, 0xda, 0x9c, 0x4c, 0x8e, 0x65,
+ 0x3e, 0x42, 0x19, 0x40, 0x5f, 0x47, 0xe8, 0x50,
+ 0x0c, 0xb3, 0xc5, 0x6e, 0x0e, 0xe6, 0x6b, 0xe7,
+ 0xb9, 0x1f, 0xe0, 0x66, 0x2a, 0x38, 0x8e, 0x38,
+ 0xa8, 0x7f, 0xfd, 0x2c, 0x92, 0xf8, 0xf2, 0x91,
+ 0x36, 0x90, 0xfd, 0x14, 0x1d, 0x07, 0xae, 0x71,
+ 0x7f, 0x3c, 0xaa, 0xd6, 0xc4, 0x10, 0x20, 0xe5,
+ 0xd0, 0xc2, 0xa2, 0xe7, 0x5e, 0x36, 0x91, 0xf7,
+ 0xff, 0x27, 0x4e, 0x81, 0xc0, 0x85, 0xab, 0x0a,
+ 0x46, 0x88, 0xe7, 0x32, 0xce, 0x83, 0x4a, 0x09,
+ 0x72, 0xb3, 0xae, 0xdd, 0x65, 0xf8, 0x13, 0xd0,
+ 0x9f, 0x4f, 0x45, 0x83, 0xfa, 0x41, 0x1f, 0x8d,
+ 0xfe, 0x96, 0xbd, 0xe0, 0x32, 0x5c, 0xb9, 0x87,
+ 0xd8, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ },
+ {
+ 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15,
+ 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37,
+ 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88,
+ 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c,
+ 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b,
+ 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e,
+ 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86,
+ 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7,
+ 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71,
+ 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23,
+ 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9,
+ 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf,
+ 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71,
+ 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15,
+ 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a,
+ 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31,
+ 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f,
+ 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b,
+ 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b,
+ 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9,
+ 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0,
+ 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb,
+ 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9,
+ 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2,
+ 0x8c, 0x9d, 0x4a, 0x62, 0x5d, 0xd1, 0x34, 0x5b,
+ 0x56, 0x44, 0x04, 0x10, 0x9e, 0x43, 0x7f, 0x62,
+ 0x18, 0x1f, 0x5d, 0x68, 0x8f, 0xa8, 0x26, 0xab,
+ 0xd4, 0xf9, 0xa6, 0xef, 0xfa, 0x3f, 0x41, 0x09,
+ 0xae, 0x44, 0x8f, 0x9e, 0xd3, 0x4c, 0xc5, 0x7b,
+ 0x35, 0x50, 0x7f, 0xc0, 0xba, 0xd9, 0x42, 0xe9,
+ 0x89, 0x9b, 0x4f, 0x30, 0x40, 0x02, 0x96, 0xed,
+ 0x7c, 0x67, 0x7e, 0xab, 0x26, 0x3b, 0xc9, 0x65,
+ 0xf6, 0x16, 0x9b, 0x7e, 0x28, 0xf2, 0xed, 0xb9,
+ 0xfd, 0x9d, 0xe4, 0x68, 0x67, 0x65, 0xf2, 0x5d,
+ 0x54, 0x43, 0xd5, 0x2d, 0xfd, 0xf4, 0x2d, 0xfd,
+ 0x49, 0x00, 0xca, 0x35, 0xc9, 0x87, 0x7c, 0xd4,
+ 0xfe, 0xfb, 0x9d, 0x8d, 0xda, 0x1c, 0x54, 0xc2,
+ 0x26, 0xed, 0xe5, 0xaa, 0xde, 0xa9, 0x41, 0x88,
+ 0x88, 0xec, 0x17, 0xab, 0x12, 0x00, 0xc4, 0x71,
+ 0x8f, 0xe4, 0x5a, 0xf9, 0x55, 0xe6, 0xd9, 0x56,
+ 0x45, 0x86, 0x6a, 0x1c, 0xd1, 0x09, 0xb1, 0xfc,
+ 0x0d, 0xd0, 0x15, 0x2c, 0xa1, 0xb6, 0xe9, 0x95,
+ 0x01, 0xe1, 0x71, 0xb3, 0xb7, 0xbf, 0x57, 0xd5,
+ 0x15, 0xec, 0xe7, 0xde, 0x85, 0x90, 0xb2, 0x47,
+ 0x51, 0x0f, 0x0f, 0x9b, 0xcf, 0x21, 0x0c, 0x77,
+ 0x21, 0xe7, 0xbb, 0x44, 0xbb, 0x70, 0xbf, 0xfb,
+ 0x29, 0x58, 0x50, 0xdc, 0x01, 0xfb, 0x2b, 0xdf,
+ 0x8d, 0x74, 0xc5, 0x89, 0xe1, 0x29, 0xcd, 0x97,
+ 0xe5, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01,
+ },
+ {
+ 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15,
+ 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37,
+ 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88,
+ 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c,
+ 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b,
+ 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e,
+ 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86,
+ 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7,
+ 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71,
+ 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23,
+ 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9,
+ 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf,
+ 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71,
+ 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15,
+ 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a,
+ 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31,
+ 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f,
+ 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b,
+ 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b,
+ 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9,
+ 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0,
+ 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb,
+ 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9,
+ 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2,
+ 0x8c, 0x9d, 0x57, 0xb9, 0x1b, 0x5d, 0x82, 0x02,
+ 0x77, 0x8c, 0x96, 0x3e, 0x36, 0x0c, 0x17, 0x8f,
+ 0x61, 0x3b, 0x3f, 0x17, 0x7d, 0x1d, 0xa9, 0x03,
+ 0xf8, 0x57, 0x4b, 0xf7, 0x48, 0xb8, 0x65, 0xfc,
+ 0xbb, 0xd4, 0x2b, 0x19, 0x06, 0xa3, 0x82, 0x61,
+ 0xe0, 0x21, 0x62, 0x31, 0xb4, 0xd2, 0x5b, 0xf0,
+ 0x4b, 0x21, 0xcd, 0xca, 0x84, 0x84, 0xc0, 0x1b,
+ 0x55, 0x4d, 0xf0, 0x32, 0xc9, 0x37, 0x57, 0xd9,
+ 0x63, 0x1b, 0x00, 0x65, 0x61, 0x93, 0x1d, 0xa4,
+ 0xb0, 0x86, 0xef, 0x4f, 0x15, 0x88, 0xf8, 0x58,
+ 0xcd, 0xfb, 0xc6, 0x2b, 0x09, 0xcd, 0x9a, 0x32,
+ 0x44, 0x97, 0x8e, 0xea, 0xfa, 0x3c, 0x5a, 0x7d,
+ 0x94, 0xda, 0x09, 0x94, 0x27, 0xa0, 0xc5, 0x57,
+ 0xa8, 0x2d, 0xc5, 0xc4, 0xdf, 0x7c, 0x5f, 0xb4,
+ 0x9b, 0x27, 0x0b, 0x62, 0xc1, 0x55, 0xcc, 0x55,
+ 0xb6, 0xfa, 0xf8, 0x70, 0x66, 0x48, 0xba, 0x21,
+ 0xe5, 0x9c, 0x27, 0x3f, 0x6b, 0xc5, 0xf3, 0x00,
+ 0x03, 0x52, 0x90, 0x06, 0xa3, 0xb9, 0xda, 0xee,
+ 0x87, 0xab, 0x78, 0x96, 0x93, 0x69, 0x34, 0x5a,
+ 0x04, 0x77, 0xcf, 0x0d, 0x20, 0xa3, 0xd7, 0x42,
+ 0x27, 0x8d, 0x59, 0xfe, 0x89, 0x67, 0x3e, 0x5d,
+ 0xe0, 0x0c, 0xb6, 0xad, 0xf0, 0x36, 0x09, 0x60,
+ 0xc1, 0x7c, 0xce, 0x2d, 0xe4, 0x75, 0xaa, 0xf5,
+ 0xa3, 0x5f, 0x72, 0x3b, 0xf2, 0x73, 0xb4, 0x9b,
+ 0xb5, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02,
+ },
+ {
+ 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15,
+ 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37,
+ 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88,
+ 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c,
+ 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b,
+ 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e,
+ 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86,
+ 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7,
+ 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71,
+ 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23,
+ 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9,
+ 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf,
+ 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71,
+ 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15,
+ 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a,
+ 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31,
+ 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f,
+ 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b,
+ 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b,
+ 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9,
+ 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0,
+ 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb,
+ 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9,
+ 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2,
+ 0x8c, 0x9d, 0x24, 0x8b, 0xa0, 0xb7, 0xf0, 0xd8,
+ 0xea, 0x6e, 0x24, 0x61, 0x57, 0xfa, 0xd8, 0xd3,
+ 0xf8, 0x10, 0xaf, 0x7e, 0x45, 0xc5, 0xea, 0x7d,
+ 0xa4, 0x0d, 0x14, 0x2b, 0x4c, 0xc9, 0x64, 0x48,
+ 0x12, 0xf1, 0x36, 0x2d, 0xef, 0x33, 0xb6, 0x74,
+ 0x4c, 0xdf, 0xd5, 0x59, 0x80, 0xa4, 0xfc, 0xa5,
+ 0x7c, 0x09, 0x24, 0xd7, 0xd5, 0xdf, 0xd6, 0xa5,
+ 0x2f, 0xa3, 0x49, 0x8a, 0xa3, 0x23, 0xa6, 0x11,
+ 0x76, 0x0e, 0xe3, 0xcd, 0xd7, 0x64, 0x0f, 0xbe,
+ 0x3f, 0x99, 0x0d, 0x05, 0x14, 0xd7, 0xa8, 0x2b,
+ 0x79, 0x6d, 0xb3, 0x12, 0x76, 0x93, 0xae, 0x35,
+ 0xbe, 0x81, 0xb7, 0xd2, 0xcf, 0x39, 0xd2, 0xad,
+ 0xb9, 0xc4, 0x3f, 0xfb, 0xe2, 0xc3, 0x32, 0x46,
+ 0x4d, 0x0b, 0x84, 0x8a, 0x2c, 0x77, 0x2f, 0x49,
+ 0xac, 0x96, 0xe0, 0x30, 0xeb, 0x38, 0x8f, 0x2f,
+ 0x01, 0x1b, 0xf3, 0x3c, 0xb5, 0xee, 0x8c, 0x62,
+ 0x9c, 0x9d, 0x0d, 0xb4, 0x57, 0x6c, 0x8d, 0xf5,
+ 0xe2, 0x16, 0xc0, 0x62, 0x9c, 0x52, 0x49, 0xf8,
+ 0xc7, 0xac, 0xb0, 0xa0, 0xe8, 0x81, 0xd9, 0x05,
+ 0x87, 0xda, 0x09, 0x6f, 0xf7, 0x95, 0xc7, 0x68,
+ 0x69, 0xbb, 0x68, 0xf4, 0x76, 0x3e, 0x2f, 0xd1,
+ 0x9d, 0x18, 0xc1, 0x47, 0x59, 0x3d, 0x56, 0x57,
+ 0x26, 0x10, 0x64, 0xc5, 0x9c, 0x35, 0xe8, 0x58,
+ 0x1b, 0x0e, 0x20, 0x97, 0xdf, 0xfa, 0xcc, 0x88,
+ 0xa0, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03,
+ },
+}, expected_key[4][KEY_LENGTH] = {
+ {
+ 0xca, 0xe4, 0x2e, 0xba, 0x74, 0x55, 0x4f, 0x10,
+ 0x4f, 0x72, 0xa4, 0x3a, 0x0b, 0x00, 0xa0, 0xc8,
+ 0xd8, 0x8f, 0xa7, 0x11, 0x8d, 0x56, 0x2a, 0x72,
+ 0xe4, 0xa8, 0xd2, 0x69, 0xf9, 0xd8, 0xa0, 0xf1,
+ },
+ {
+ 0x63, 0x1d, 0x52, 0x11, 0xed, 0x13, 0xa8, 0x8e,
+ 0x4c, 0xe0, 0x06, 0x2e, 0xe6, 0xa3, 0xa1, 0xf6,
+ 0x7a, 0xf1, 0xc4, 0x1e, 0x18, 0xbf, 0x74, 0xe2,
+ 0x52, 0x23, 0x86, 0x56, 0x1b, 0x0e, 0x78, 0x7d,
+ },
+ {
+ 0x3f, 0xb2, 0xb6, 0x39, 0xe3, 0x54, 0x41, 0x04,
+ 0xf3, 0x48, 0x5f, 0x50, 0xf7, 0x8b, 0xd6, 0xe7,
+ 0x8e, 0x45, 0xc2, 0xee, 0xbf, 0xdd, 0xfd, 0x83,
+ 0x34, 0x7a, 0x9a, 0x8a, 0xf3, 0x4d, 0xd4, 0xc6,
+ },
+ {
+ 0x98, 0x76, 0x29, 0xe2, 0x27, 0xf0, 0x0e, 0x2a,
+ 0xff, 0xc8, 0xfd, 0x3b, 0xcf, 0xcb, 0x9f, 0x6d,
+ 0xe9, 0x80, 0xfe, 0x6e, 0xd9, 0xc0, 0x43, 0x29,
+ 0xc5, 0x53, 0x5f, 0xf7, 0x86, 0x33, 0x3b, 0x70,
+ },
+}, expected_tag[4][TAG_LENGTH] = {
+ {
+ 0x10, 0xa4, 0x14, 0x19, 0xec, 0xf1, 0x9b, 0x6d,
+ 0xe5, 0xe3, 0x12, 0x2b, 0x06, 0xee, 0x5b, 0xac,
+ 0xa7, 0x11, 0x0b, 0x70, 0x15, 0xfd, 0x47, 0xa4,
+ 0xb9, 0xd7, 0x45, 0x19, 0x4b, 0xde, 0x92, 0x5d,
+ },
+ {
+ 0x75, 0x54, 0x5b, 0x6a, 0xfb, 0x30, 0x43, 0x3b,
+ 0x29, 0x94, 0x58, 0xee, 0x62, 0xd9, 0x62, 0x2b,
+ 0x61, 0x88, 0xed, 0xce, 0xc9, 0x95, 0x1e, 0x82,
+ 0x9e, 0xd6, 0xd1, 0x90, 0x23, 0xc6, 0x90, 0x65,
+ },
+ {
+ 0x2f, 0x8b, 0x64, 0xa4, 0x21, 0xe8, 0x74, 0x93,
+ 0xa2, 0x3a, 0x6a, 0xa9, 0x0d, 0x7e, 0xb0, 0x80,
+ 0xce, 0x74, 0x80, 0xb9, 0x06, 0xae, 0xec, 0x86,
+ 0x4c, 0x32, 0x99, 0xf9, 0x48, 0x1f, 0x0c, 0xad,
+ },
+ {
+ 0x39, 0x9e, 0xf6, 0xc3, 0x1e, 0xca, 0x02, 0xb2,
+ 0x6a, 0xe2, 0xa6, 0x47, 0x0b, 0xb5, 0xb6, 0xaf,
+ 0x46, 0xd7, 0x99, 0x13, 0xc6, 0x07, 0xa8, 0x80,
+ 0x73, 0x35, 0x75, 0x73, 0xfd, 0x03, 0x5b, 0x3e,
+ },
+};
+
+static void generate_seed(struct iovec *seed) {
+ assert(iovec_is_set(seed));
+
+ uint8_t *p = seed->iov_base;
+ for (size_t i = 0; i < seed->iov_len; i++)
+ *p++ = (uint8_t) i;
+}
+
+TEST(fsprg) {
+ size_t n_states = 20;
+
+ /* Generating private keys is slow when running on valgrind or with sanitizers. */
+#if HAVE_VALGRIND_VALGRIND_H
+ if (RUNNING_ON_VALGRIND)
+ n_states = ELEMENTSOF(expected_state);
+#endif
+#if HAS_FEATURE_ADDRESS_SANITIZER
+ n_states = ELEMENTSOF(expected_state);
+#endif
+
+ struct iovec *states = new0(struct iovec, n_states);
+ CLEANUP_ARRAY(states, n_states, iovec_array_free);
+
+ if (initialize_libgcrypt(false) >= 0) {
+ struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN),
+ msk = IOVEC_ALLOCA(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)),
+ mpk = IOVEC_ALLOCA(FSPRG_mpkinbytes(FSPRG_RECOMMENDED_SECPAR)),
+ state = IOVEC_ALLOCA(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR)),
+ key = IOVEC_ALLOCA(KEY_LENGTH);
+
+ generate_seed(&seed);
+
+ /* Generate secret keys */
+ ASSERT_OK(FSPRG_GenMK(msk.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len, FSPRG_RECOMMENDED_SECPAR));
+
+ /* Generate states by FSPRG_Seek() */
+ for (size_t i = 0; i < n_states; i++) {
+ ASSERT_OK(FSPRG_Seek(state.iov_base, i, msk.iov_base, seed.iov_base, seed.iov_len));
+
+ /* Verify state */
+ if (i < ELEMENTSOF(expected_state))
+ ASSERT_TRUE(iovec_equal(&state, &IOVEC_MAKE(expected_state[i], sizeof(expected_state[i]))));
+
+ /* Verify key */
+ ASSERT_OK(FSPRG_GetKey(state.iov_base, key.iov_base, key.iov_len, 0));
+ if (i < ELEMENTSOF(expected_key))
+ ASSERT_TRUE(iovec_equal(&key, &IOVEC_MAKE(expected_key[i], sizeof(expected_key[i]))));
+
+ /* Verify epoch */
+ ASSERT_EQ(FSPRG_GetEpoch(state.iov_base), (uint64_t) i);
+
+ ASSERT_NOT_NULL(iovec_memdup(&state, &states[i]));
+ }
+
+ /* Generate state by FSPRG_GetState0() */
+ ASSERT_OK(FSPRG_GenState0(state.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len));
+ ASSERT_TRUE(iovec_equal(&state, &states[0]));
+
+ /* Generate states by FSPRG_Evolve() */
+ for (size_t i = 1; i < n_states; i++) {
+ ASSERT_OK(FSPRG_Evolve(state.iov_base));
+ ASSERT_TRUE(iovec_equal(&state, &states[i]));
+ }
+ }
+
+ if (dlopen_libcrypto(LOG_DEBUG) >= 0) {
+ struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN),
+ state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR)),
+ key = IOVEC_ALLOCA(32);
+
+ generate_seed(&seed);
+
+ /* Generate states by fsprg_generate_state() */
+ for (size_t i = 0; i < n_states; i++) {
+ ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, i, &seed, &state));
+
+ /* Verify state */
+ if (i < ELEMENTSOF(expected_state))
+ ASSERT_EQ(iovec_memcmp(&state, &IOVEC_MAKE(expected_state[i], sizeof(expected_state[i]))), 0);
+
+ /* Verify key */
+ ASSERT_OK(fsprg_get_key(&state, &key));
+ if (i < ELEMENTSOF(expected_key))
+ ASSERT_EQ(iovec_memcmp(&key, &IOVEC_MAKE(expected_key[i], sizeof(expected_key[i]))), 0);
+
+ /* Verify epoch */
+ uint64_t epoch;
+ ASSERT_OK(fsprg_get_epoch(&state, &epoch));
+ ASSERT_EQ(epoch, (uint64_t) i);
+
+ if (iovec_is_set(&states[i]))
+ ASSERT_EQ(iovec_memcmp(&state, &states[i]), 0);
+ else
+ ASSERT_NOT_NULL(iovec_memdup(&state, &states[i]));
+ }
+
+ /* Generate state by fsprg_generate_state(0) */
+ ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, 0, &seed, &state));
+ ASSERT_EQ(iovec_memcmp(&state, &states[0]), 0);
+
+ /* Generate states by fsprg_evolve() */
+ for (unsigned i = 1; i < n_states; i++) {
+ ASSERT_OK(fsprg_evolve(&state));
+ ASSERT_EQ(iovec_memcmp(&state, &states[i]), 0);
+ }
+ }
+}
+
+TEST(hmac) {
+ /* This emulates what journald and journalctl does.
+ * 1. journalctl generates random seed, calculate initial state, and write the state in
+ * /var/log/journal/$MACHINE_ID/fss.
+ * 2. journald loads the fss file when a new journal file is created by journal_file_fss_load().
+ * 3. Allocate HMAC with SHA256 by journal_file_hmac_setup().
+ * 4. Write inital tag in journal file by journal_file_append_first_tag().
+ * 5. For each journal entry,
+ * - initialize HMAC with fsprg key by journal_file_hmac_start(),
+ * - put each object data to the HMAC by journal_file_hmac_put_object(),
+ * - finalize HMAC and write the value with the fsprg epoch to a tag object by
+ * journal_file_maybe_append_tag(). */
+
+ size_t n_tags = 20;
+ struct iovec *tags = new0(struct iovec, n_tags);
+ CLEANUP_ARRAY(tags, n_tags, iovec_array_free);
+
+#if HAVE_GCRYPT
+ if (initialize_libgcrypt(false) >= 0) {
+ struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN),
+ msk = IOVEC_ALLOCA(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)),
+ mpk = IOVEC_ALLOCA(FSPRG_mpkinbytes(FSPRG_RECOMMENDED_SECPAR)),
+ state = IOVEC_ALLOCA(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR)),
+ key = IOVEC_ALLOCA(KEY_LENGTH),
+ tag = IOVEC_ALLOCA(TAG_LENGTH);
+
+ generate_seed(&seed);
+
+ /* Generate secret keys */
+ ASSERT_OK(FSPRG_GenMK(msk.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len, FSPRG_RECOMMENDED_SECPAR));
+
+ /* Calculate initial state */
+ ASSERT_OK(FSPRG_GenState0(state.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len));
+
+ /* Allocate HMAC */
+ _cleanup_(sym_gcry_md_closep) gcry_md_hd_t hmac = NULL;
+ ASSERT_EQ(gcry_err_code(sym_gcry_md_open(&hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC)), (gcry_err_code_t) GPG_ERR_NO_ERROR);
+
+ for (size_t i = 0; i < n_tags; i++) {
+ /* Reset the previous state */
+ sym_gcry_md_reset(hmac);
+
+ /* Initialize HMAC with fsprg key */
+ ASSERT_OK(FSPRG_GetKey(state.iov_base, key.iov_base, key.iov_len, 0));
+ ASSERT_EQ(gcry_err_code(sym_gcry_md_setkey(hmac, key.iov_base, key.iov_len)), (gcry_err_code_t) GPG_ERR_NO_ERROR);
+
+ /* Put dummy data */
+ _cleanup_free_ char *str = ASSERT_NOT_NULL(asprintf_safe("dummy data: %zu, hoge hoge hoge hoge hoge", i));
+ sym_gcry_md_write(hmac, str, strlen(str));
+
+ /* Finalize HMAC and generate tag. */
+ memcpy(tag.iov_base, sym_gcry_md_read(hmac, 0), tag.iov_len);
+
+ /* Verify tag */
+ if (i < ELEMENTSOF(expected_tag))
+ ASSERT_TRUE(iovec_equal(&tag, &IOVEC_MAKE(expected_tag[i], sizeof(expected_tag[i]))));
+
+ /* Save tag */
+ ASSERT_NOT_NULL(iovec_memdup(&tag, &tags[i]));
+
+ /* Increment epoch */
+ ASSERT_OK(FSPRG_Evolve(state.iov_base));
+ }
+ }
+#endif
+
+#if HAVE_OPENSSL
+ if (dlopen_libcrypto(LOG_DEBUG) >= 0) {
+ struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN),
+ state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR)),
+ key = IOVEC_ALLOCA(KEY_LENGTH),
+ tag = IOVEC_ALLOCA(TAG_LENGTH);
+
+ generate_seed(&seed);
+
+ /* Calculate initial state */
+ ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, 0, &seed, &state));
+
+ /* Allocate HMAC */
+ _cleanup_(EVP_MAC_freep) EVP_MAC *hmac = ASSERT_NOT_NULL(sym_EVP_MAC_fetch(NULL, "HMAC", NULL));
+ _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *hmac_ctx = ASSERT_NOT_NULL(sym_EVP_MAC_CTX_new(hmac));
+ _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = ASSERT_NOT_NULL(sym_OSSL_PARAM_BLD_new());
+ ASSERT_OK_POSITIVE(sym_OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, "SHA256", 0));
+ _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = ASSERT_NOT_NULL(sym_OSSL_PARAM_BLD_to_param(bld));
+
+ for (size_t i = 0; i < n_tags; i++) {
+ /* Initialize HMAC with fsprg key */
+ ASSERT_OK(fsprg_get_key(&state, &key));
+ ASSERT_OK_POSITIVE(sym_EVP_MAC_init(hmac_ctx, key.iov_base, key.iov_len, params));
+
+ /* Put dummy data */
+ _cleanup_(iovec_done) struct iovec data =
+ IOVEC_MAKE_STRING(ASSERT_NOT_NULL(asprintf_safe("dummy data: %zu, hoge hoge hoge hoge hoge", i)));
+ ASSERT_OK_POSITIVE(sym_EVP_MAC_update(hmac_ctx, data.iov_base, data.iov_len));
+
+ /* Finalize HMAC and generate tag. */
+ size_t len;
+ ASSERT_OK_POSITIVE(sym_EVP_MAC_final(hmac_ctx, tag.iov_base, &len, tag.iov_len));
+ ASSERT_EQ(len, tag.iov_len);
+
+ /* Verify tag */
+ if (i < ELEMENTSOF(expected_tag))
+ ASSERT_TRUE(iovec_equal(&tag, &IOVEC_MAKE(expected_tag[i], sizeof(expected_tag[i]))));
+
+ /* Compare with the tag generated by gcrypt */
+ if (iovec_is_set(&tags[i]))
+ ASSERT_TRUE(iovec_equal(&tag, &tags[i]));
+
+ /* Increment epoch */
+ ASSERT_OK(fsprg_evolve(&state));
+ }
+ }
+#endif
+}
+
+static int intro(void) {
+ if (DLOPEN_GCRYPT(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0 &&
+ DLOPEN_LIBCRYPTO(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0)
+ return EXIT_TEST_SKIP;
+
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);
diff --git a/test/units/TEST-04-JOURNAL.fss.sh b/test/units/TEST-04-JOURNAL.fss.sh
index 50f0608280..b3f27195a7 100755
--- a/test/units/TEST-04-JOURNAL.fss.sh
+++ b/test/units/TEST-04-JOURNAL.fss.sh
@@ -5,8 +5,8 @@ set -o pipefail
# Forward Secure Sealing
-if ! journalctl --version | grep -F +GCRYPT >/dev/null; then
- echo "Built without gcrypt, skipping the FSS tests"
+if ! journalctl --version | grep -F +OPENSSL >/dev/null; then
+ echo "Built without openssl, skipping the FSS tests"
exit 0
fi