return r;
o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
+ o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
/* Add the tag object itself, so that we can protect its
* header. This will exclude the actual hash value in it */
return 0;
}
-static int journal_file_hmac_start(JournalFile *f) {
+int journal_file_hmac_start(JournalFile *f) {
uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
-
assert(f);
if (!f->authenticate)
}
}
+int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
+ void *msk;
+ uint64_t epoch;
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ assert(f->fsprg_seed);
+
+ if (f->fsprg_state) {
+ /* Cheaper... */
+
+ epoch = FSPRG_GetEpoch(f->fsprg_state);
+ if (goal == epoch)
+ return 0;
+
+ if (goal == epoch+1) {
+ FSPRG_Evolve(f->fsprg_state);
+ return 0;
+ }
+ } else {
+ f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
+ f->fsprg_state = malloc(f->fsprg_state_size);
+
+ if (!f->fsprg_state)
+ return -ENOMEM;
+ }
+
+ log_debug("Seeking FSPRG key to %llu.", (unsigned long long) goal);
+
+ msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
+ FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
+ FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
+ return 0;
+}
+
int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
int r;
switch (o->object.type) {
case OBJECT_DATA:
- /* All but: hash and payload are mutable */
+ /* All but hash and payload are mutable */
gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
break;
case OBJECT_TAG:
/* All but the tag itself */
gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
+ gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
break;
default:
return -EINVAL;
return r;
/* All but state+reserved, boot_id, arena_size,
- * tail_object_offset, n_objects, n_entries, tail_seqnum,
+ * 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, header_tag */
+ * tail_entry_monotonic, n_data, n_fields, n_tags,
+ * n_entry_arrays. */
gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
- gcry_md_write(f->hmac, &f->header->head_entry_seqnum, offsetof(Header, head_entry_realtime) - offsetof(Header, head_entry_seqnum));
return 0;
}
int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime);
int journal_file_append_first_tag(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, int type, uint64_t p);
int journal_file_setup_hmac(JournalFile *f);
bool journal_file_fsprg_enabled(JournalFile *f);
+
+int journal_file_fsprg_seek(JournalFile *f, uint64_t epoch);
_packed_ struct TagObject {
ObjectHeader object;
uint64_t seqnum;
+ uint64_t epoch;
uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */
};
r = journal_file_refresh_header(f);
if (r < 0)
goto fail;
-
- r = journal_file_setup_hmac(f);
- if (r < 0)
- goto fail;
}
+ r = journal_file_setup_hmac(f);
+ if (r < 0)
+ goto fail;
+
if (newly_created) {
r = journal_file_setup_field_hash_table(f);
if (r < 0)
/* FIXME:
*
- * - verify FSPRG
+ * - write tag only if non-tag objects have been written
+ * - change terms
+ * - write bit mucking test
+ *
* - Allow building without libgcrypt
* - check with sparse
* - 64bit conversions
int journal_file_verify(JournalFile *f, const char *seed) {
int r;
Object *o;
- uint64_t p = 0, last_tag = 0;
- uint64_t n_tags = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
+ uint64_t p = 0, last_tag = 0, last_epoch = 0;
+ uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
sd_id128_t entry_boot_id;
bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
- uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0;
+ uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0;
usec_t last_usec = 0;
int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
char data_path[] = "/var/tmp/journal-data-XXXXXX",
n_entry_arrays++;
break;
- case OBJECT_TAG:
+ case OBJECT_TAG: {
+ uint64_t q;
+
if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
log_error("Tag object without authentication at %llu", (unsigned long long) p);
r = -EBADMSG;
goto fail;
}
+ if (le64toh(o->tag.epoch) < last_epoch) {
+ log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
+ r = -EBADMSG;
+ goto fail;
+ }
+
+ /* 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));
+ if (r < 0)
+ goto fail;
+
+ r = journal_file_hmac_start(f);
+ if (r < 0)
+ goto fail;
+
+ if (last_tag == 0) {
+ r = journal_file_hmac_put_header(f);
+ if (r < 0)
+ goto fail;
+
+ q = le64toh(f->header->header_size);
+ } else
+ q = last_tag;
+
+ while (q <= p) {
+ r = journal_file_move_to_object(f, -1, q, &o);
+ if (r < 0)
+ goto fail;
+
+ r = journal_file_hmac_put_object(f, -1, q);
+ if (r < 0)
+ goto fail;
+
+ q = q + ALIGN64(le64toh(o->object.size));
+ }
+
+ /* Position might have changed, let's reposition things */
+ r = journal_file_move_to_object(f, -1, p, &o);
+ if (r < 0)
+ goto fail;
+
+ if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
+ log_error("Tag did not authenticate at %llu", (unsigned long long) p);
+ r = -EBADMSG;
+ goto fail;
+ }
+
+ f->hmac_running = false;
+
+ last_tag = p + ALIGN64(le64toh(o->object.size));
n_tags ++;
break;
+ }
default:
n_weird ++;
static const char *arg_directory = NULL;
static int arg_priorities = 0xFF;
static const char *arg_verify_seed = NULL;
+static usec_t arg_evolve = DEFAULT_FSPRG_INTERVAL_USEC;
static enum {
ACTION_SHOW,
printf("%s [OPTIONS...] [MATCH]\n\n"
"Send control commands to or query the journal.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --no-pager Do not pipe output into a pager\n"
- " -a --all Show all fields, including long and unprintable\n"
- " -f --follow Follow journal\n"
- " -n --lines=INTEGER Journal entries to show\n"
- " --no-tail Show all lines, even in follow mode\n"
- " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
- " verbose, export, json, cat)\n"
- " -q --quiet Don't show privilege warning\n"
- " -l --local Only local entries\n"
- " -b --this-boot Show data only from current boot\n"
- " -D --directory=PATH Show journal files from directory\n"
- " -p --priority=RANGE Show only messages within the specified priority range\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " -a --all Show all fields, including long and unprintable\n"
+ " -f --follow Follow journal\n"
+ " -n --lines=INTEGER Journal entries to show\n"
+ " --no-tail Show all lines, even in follow mode\n"
+ " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
+ " verbose, export, json, cat)\n"
+ " -q --quiet Don't show privilege warning\n"
+ " -l --local Only local entries\n"
+ " -b --this-boot Show data only from current boot\n"
+ " -D --directory=PATH Show journal files from directory\n"
+ " -p --priority=RANGE Show only messages within the specified priority range\n\n"
"Commands:\n"
- " --new-id128 Generate a new 128 Bit ID\n"
- " --header Show journal header information\n"
- " --verify Verify journal file consistency\n"
- " --verify-seed=SEED Specify FSPRG seed for verification\n"
- " --setup-keys Generate new FSPRG key and seed\n",
+ " --new-id128 Generate a new 128 Bit ID\n"
+ " --header Show journal header information\n"
+ " --verify Verify journal file consistency\n"
+ " --verify-seed=SEED Specify FSPRG seed for verification\n"
+ " --setup-keys Generate new FSPRG key and seed\n"
+ " --evolve=TIME How of to evolve FSPRG keys\n",
program_invocation_short_name);
return 0;
ARG_HEADER,
ARG_SETUP_KEYS,
ARG_VERIFY,
- ARG_VERIFY_SEED
+ ARG_VERIFY_SEED,
+ ARG_EVOLVE
};
static const struct option options[] = {
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
{ "verify", no_argument, NULL, ARG_VERIFY },
{ "verify-seed", required_argument, NULL, ARG_VERIFY_SEED },
+ { "evolve", required_argument, NULL, ARG_EVOLVE },
{ NULL, 0, NULL, 0 }
};
arg_verify_seed = optarg;
break;
+ case ARG_EVOLVE:
+ r = parse_usec(optarg, &arg_evolve);
+ if (r < 0 || arg_evolve <= 0) {
+ log_error("Failed to parse evolve interval: %s", optarg);
+ return -EINVAL;
+ }
+ break;
+
case 'p': {
const char *dots;
sd_id128_t machine, boot;
char *p = NULL, *k = NULL;
struct FSPRGHeader h;
- uint64_t n, interval;
+ uint64_t n;
r = sd_id128_get_machine(&machine);
if (r < 0) {
log_info("Generating evolving key...");
FSPRG_GenState0(state, mpk, seed, seed_size);
- interval = DEFAULT_FSPRG_INTERVAL_USEC;
n = now(CLOCK_REALTIME);
- n /= interval;
+ n /= arg_evolve;
close_nointr_nofail(fd);
fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
h.machine_id = machine;
h.boot_id = boot;
h.header_size = htole64(sizeof(h));
- h.fsprg_start_usec = htole64(n * interval);
- h.fsprg_interval_usec = htole64(interval);
+ h.fsprg_start_usec = htole64(n * arg_evolve);
+ h.fsprg_interval_usec = htole64(arg_evolve);
h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
h.state_size = htole64(state_size);
printf("%02x", ((uint8_t*) seed)[i]);
}
- printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
+ printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_evolve);
if (isatty(STDOUT_FILENO))
fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
char t[] = "/tmp/journal-XXXXXX";
unsigned n;
JournalFile *f;
+ const char *verification_key = argv[1];
log_set_max_level(LOG_DEBUG);
log_info("Generating...");
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0);
for (n = 0; n < N_ENTRIES; n++) {
struct iovec iovec;
log_info("Verifying...");
assert_se(journal_file_open("test.journal", O_RDONLY, 0666, false, false, NULL, NULL, NULL, &f) == 0);
- assert_se(journal_file_verify(f, NULL) >= 0);
+ assert_se(journal_file_verify(f, verification_key) >= 0);
journal_file_close(f);
log_info("Exiting...");