journal: replace gcrypt with openssl

This commit is contained in:
Yu Watanabe
2026-06-22 16:34:35 +09:00
parent 7fae5c5bcc
commit 005e94da17
7 changed files with 118 additions and 70 deletions

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -94,6 +94,7 @@ executables += [
'link_with' : journalctl_link_with,
'dependencies' : [
liblz4_cflags,
libopenssl_cflags,
libxz_cflags,
libzstd_cflags,
],

View File

@@ -5,9 +5,9 @@
#include <sys/stat.h>
#include "alloc-util.h"
#include "crypto-util.h"
#include "fd-util.h"
#include "fsprg.h"
#include "gcrypt-util.h"
#include "fsprg-openssl.h"
#include "hexdecoct.h"
#include "iovec-util.h"
#include "journal-authenticate.h"
@@ -18,10 +18,12 @@
#include "string-util.h"
#include "time-util.h"
#if HAVE_GCRYPT
#if HAVE_OPENSSL
struct JournalAuthContext {
gcry_md_hd_t hmac;
EVP_MAC *hmac;
EVP_MAC_CTX *hmac_ctx;
OSSL_PARAM *ossl_params;
bool hmac_running;
FSSHeader *fss_file;
@@ -45,8 +47,12 @@ static JournalAuthContext* journal_auth_free(JournalAuthContext *c) {
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_gcry_md_close(c->hmac);
sym_EVP_MAC_free(c->hmac);
return mfree(c);
}
@@ -111,7 +117,11 @@ static int journal_auth_load(JournalAuthContext **ret) {
if (le64toh(header->header_size) < sizeof(FSSHeader))
return -EBADMSG;
if (le64toh(header->fsprg_state_size) != FSPRG_stateinbytes(le16toh(header->fsprg_secpar)))
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;
@@ -239,9 +249,14 @@ static int journal_auth_epoch_to_realtime_usec(const JournalAuthContext *c, uint
}
static int journal_auth_next_evolve_usec(const JournalAuthContext *c, usec_t *ret) {
int r;
assert(c);
uint64_t epoch = FSPRG_GetEpoch(c->fsprg_state.iov_base);
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);
}
@@ -253,26 +268,23 @@ static int journal_auth_seek(JournalAuthContext *c, uint64_t goal) {
assert(iovec_is_set(&c->fsprg_seed));
if (iovec_is_set(&c->fsprg_state)) {
uint64_t epoch = FSPRG_GetEpoch(c->fsprg_state.iov_base);
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.iov_base);
return fsprg_evolve(&c->fsprg_state);
} else {
r = iovec_alloc(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR), &c->fsprg_state);
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);
_cleanup_(iovec_erase) struct iovec msk = IOVEC_ALLOCA(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
r = FSPRG_GenMK(msk.iov_base, NULL, c->fsprg_seed.iov_base, c->fsprg_seed.iov_len, FSPRG_RECOMMENDED_SECPAR);
if (r < 0)
return r;
return FSPRG_Seek(c->fsprg_state.iov_base, goal, msk.iov_base, c->fsprg_seed.iov_base, c->fsprg_seed.iov_len);
return fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, goal, &c->fsprg_seed, &c->fsprg_state);
}
static int journal_auth_setup(JournalAuthContext *c) {
@@ -283,13 +295,32 @@ static int journal_auth_setup(JournalAuthContext *c) {
if (c->hmac)
return 0;
r = initialize_libgcrypt(true);
r = dlopen_libcrypto(LOG_DEBUG);
if (r < 0)
return r;
if (sym_gcry_md_open(&c->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != 0)
return -EOPNOTSUPP;
_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;
}
@@ -305,20 +336,15 @@ static int journal_auth_start(JournalAuthContext *c) {
if (r < 0)
return r;
/* Prepare HMAC for next cycle */
sym_gcry_md_reset(c->hmac);
uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
CLEANUP_ERASE(key);
r = FSPRG_GetKey(c->fsprg_state.iov_base, key, sizeof(key), 0);
r = fsprg_get_key(&c->fsprg_state, &IOVEC_MAKE(key, sizeof(key)));
if (r < 0)
return r;
gcry_error_t err = sym_gcry_md_setkey(c->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));
/* 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;
@@ -331,8 +357,16 @@ static int journal_auth_end(JournalAuthContext *c, uint8_t ret[static TAG_LENGTH
if (!c->hmac_running)
return -EINVAL;
memcpy(ret, sym_gcry_md_read(c->hmac, 0), TAG_LENGTH);
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;
}
@@ -353,10 +387,14 @@ static int journal_auth_put_header(JournalAuthContext *c, JournalFile *f) {
* tail_entry_monotonic, n_data, n_fields, n_tags,
* n_entry_arrays. */
sym_gcry_md_write(c->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
sym_gcry_md_write(c->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
sym_gcry_md_write(c->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
sym_gcry_md_write(c->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
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;
}
@@ -378,25 +416,31 @@ static int journal_auth_put_object(JournalAuthContext *c, JournalFile *f, Object
} else if (type > OBJECT_UNUSED && o->object.type != type)
return -EBADMSG;
sym_gcry_md_write(c->hmac, o, offsetof(ObjectHeader, payload));
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 */
sym_gcry_md_write(c->hmac, &o->data.hash, sizeof(o->data.hash));
sym_gcry_md_write(c->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
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 */
sym_gcry_md_write(c->hmac, &o->field.hash, sizeof(o->field.hash));
sym_gcry_md_write(c->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
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 */
sym_gcry_md_write(c->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
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:
@@ -407,8 +451,10 @@ static int journal_auth_put_object(JournalAuthContext *c, JournalFile *f, Object
case OBJECT_TAG:
/* All but the tag itself */
sym_gcry_md_write(c->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
sym_gcry_md_write(c->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
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;
@@ -437,10 +483,15 @@ static int journal_auth_append_tag(JournalAuthContext *c, JournalFile *f) {
f->header->n_tags = htole64(seqnum);
o->tag.seqnum = htole64(seqnum);
o->tag.epoch = htole64(FSPRG_GetEpoch(c->fsprg_state.iov_base));
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), le64toh(o->tag.epoch));
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 */
@@ -500,7 +551,10 @@ static int journal_auth_append_tag_maybe(JournalAuthContext *c, JournalFile *f,
uint64_t goal = usec_sub_unsigned(realtime, c->fss_start_usec) / c->fss_interval_usec;
for (;;) {
uint64_t epoch = FSPRG_GetEpoch(c->fsprg_state.iov_base);
uint64_t epoch;
r = fsprg_get_epoch(&c->fsprg_state, &epoch);
if (r < 0)
return r;
if (epoch >= goal)
return 0;
@@ -508,7 +562,7 @@ static int journal_auth_append_tag_maybe(JournalAuthContext *c, JournalFile *f,
if (r < 0)
return r;
r = FSPRG_Evolve(c->fsprg_state.iov_base);
r = fsprg_evolve(&c->fsprg_state);
if (r < 0)
return r;
}
@@ -539,4 +593,4 @@ void journal_auth_init(void) {
void journal_auth_init(void) {
}
#endif /* HAVE_GCRYPT */
#endif /* HAVE_OPENSSL */

View File

@@ -344,7 +344,7 @@ executables += [
libgcrypt_cflags,
libopenssl_cflags,
],
'conditions' : ['HAVE_GCRYPT'],
'conditions' : ['HAVE_OPENSSL'],
},
test_template + {
'sources' : files(

View File

@@ -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