1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "gcrypt-util.h"
10 #include "journal-authenticate.h"
11 #include "journal-def.h"
12 #include "journal-file.h"
14 static uint64_t journal_file_tag_seqnum(JournalFile
*f
) {
19 r
= le64toh(f
->header
->n_tags
) + 1;
20 f
->header
->n_tags
= htole64(r
);
25 int journal_file_append_tag(JournalFile
*f
) {
40 r
= journal_file_append_object(f
, OBJECT_TAG
, sizeof(struct TagObject
), &o
, &p
);
44 o
->tag
.seqnum
= htole64(journal_file_tag_seqnum(f
));
45 o
->tag
.epoch
= htole64(FSPRG_GetEpoch(f
->fsprg_state
));
47 log_debug("Writing tag %"PRIu64
" for epoch %"PRIu64
"",
48 le64toh(o
->tag
.seqnum
),
49 FSPRG_GetEpoch(f
->fsprg_state
));
51 /* Add the tag object itself, so that we can protect its
52 * header. This will exclude the actual hash value in it */
53 r
= journal_file_hmac_put_object(f
, OBJECT_TAG
, o
, p
);
57 /* Get the HMAC tag and store it in the object */
58 memcpy(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
);
59 f
->hmac_running
= false;
64 int journal_file_hmac_start(JournalFile
*f
) {
65 uint8_t key
[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
74 /* Prepare HMAC for next cycle */
75 gcry_md_reset(f
->hmac
);
76 FSPRG_GetKey(f
->fsprg_state
, key
, sizeof(key
), 0);
77 gcry_md_setkey(f
->hmac
, key
, sizeof(key
));
79 f
->hmac_running
= true;
84 static int journal_file_get_epoch(JournalFile
*f
, uint64_t realtime
, uint64_t *epoch
) {
91 if (f
->fss_start_usec
== 0 ||
92 f
->fss_interval_usec
== 0)
95 if (realtime
< f
->fss_start_usec
)
98 t
= realtime
- f
->fss_start_usec
;
99 t
= t
/ f
->fss_interval_usec
;
105 static int journal_file_fsprg_need_evolve(JournalFile
*f
, uint64_t realtime
) {
106 uint64_t goal
, epoch
;
113 r
= journal_file_get_epoch(f
, realtime
, &goal
);
117 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
121 return epoch
!= goal
;
124 int journal_file_fsprg_evolve(JournalFile
*f
, uint64_t realtime
) {
125 uint64_t goal
, epoch
;
133 r
= journal_file_get_epoch(f
, realtime
, &goal
);
137 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
139 log_debug("Evolving FSPRG key from epoch %"PRIu64
" to %"PRIu64
".", epoch
, goal
);
147 FSPRG_Evolve(f
->fsprg_state
);
148 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
152 int journal_file_fsprg_seek(JournalFile
*f
, uint64_t goal
) {
161 assert(f
->fsprg_seed
);
163 if (f
->fsprg_state
) {
166 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
170 if (goal
== epoch
+1) {
171 FSPRG_Evolve(f
->fsprg_state
);
175 f
->fsprg_state_size
= FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR
);
176 f
->fsprg_state
= malloc(f
->fsprg_state_size
);
182 log_debug("Seeking FSPRG key to %"PRIu64
".", goal
);
184 msk
= alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR
));
185 FSPRG_GenMK(msk
, NULL
, f
->fsprg_seed
, f
->fsprg_seed_size
, FSPRG_RECOMMENDED_SECPAR
);
186 FSPRG_Seek(f
->fsprg_state
, goal
, msk
, f
->fsprg_seed
, f
->fsprg_seed_size
);
190 int journal_file_maybe_append_tag(JournalFile
*f
, uint64_t realtime
) {
199 realtime
= now(CLOCK_REALTIME
);
201 r
= journal_file_fsprg_need_evolve(f
, realtime
);
205 r
= journal_file_append_tag(f
);
209 r
= journal_file_fsprg_evolve(f
, realtime
);
216 int journal_file_hmac_put_object(JournalFile
*f
, ObjectType type
, Object
*o
, uint64_t p
) {
224 r
= journal_file_hmac_start(f
);
229 r
= journal_file_move_to_object(f
, type
, p
, &o
);
233 if (type
> OBJECT_UNUSED
&& o
->object
.type
!= type
)
237 gcry_md_write(f
->hmac
, o
, offsetof(ObjectHeader
, payload
));
239 switch (o
->object
.type
) {
242 /* All but hash and payload are mutable */
243 gcry_md_write(f
->hmac
, &o
->data
.hash
, sizeof(o
->data
.hash
));
244 gcry_md_write(f
->hmac
, o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(DataObject
, payload
));
249 gcry_md_write(f
->hmac
, &o
->field
.hash
, sizeof(o
->field
.hash
));
250 gcry_md_write(f
->hmac
, o
->field
.payload
, le64toh(o
->object
.size
) - offsetof(FieldObject
, payload
));
255 gcry_md_write(f
->hmac
, &o
->entry
.seqnum
, le64toh(o
->object
.size
) - offsetof(EntryObject
, seqnum
));
258 case OBJECT_FIELD_HASH_TABLE
:
259 case OBJECT_DATA_HASH_TABLE
:
260 case OBJECT_ENTRY_ARRAY
:
261 /* Nothing: everything is mutable */
265 /* All but the tag itself */
266 gcry_md_write(f
->hmac
, &o
->tag
.seqnum
, sizeof(o
->tag
.seqnum
));
267 gcry_md_write(f
->hmac
, &o
->tag
.epoch
, sizeof(o
->tag
.epoch
));
276 int journal_file_hmac_put_header(JournalFile
*f
) {
284 r
= journal_file_hmac_start(f
);
288 /* All but state+reserved, boot_id, arena_size,
289 * tail_object_offset, n_objects, n_entries,
290 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
291 * head_entry_realtime, tail_entry_realtime,
292 * tail_entry_monotonic, n_data, n_fields, n_tags,
295 gcry_md_write(f
->hmac
, f
->header
->signature
, offsetof(Header
, state
) - offsetof(Header
, signature
));
296 gcry_md_write(f
->hmac
, &f
->header
->file_id
, offsetof(Header
, boot_id
) - offsetof(Header
, file_id
));
297 gcry_md_write(f
->hmac
, &f
->header
->seqnum_id
, offsetof(Header
, arena_size
) - offsetof(Header
, seqnum_id
));
298 gcry_md_write(f
->hmac
, &f
->header
->data_hash_table_offset
, offsetof(Header
, tail_object_offset
) - offsetof(Header
, data_hash_table_offset
));
303 int journal_file_fss_load(JournalFile
*f
) {
315 r
= sd_id128_get_machine(&machine
);
319 if (asprintf(&p
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss",
320 SD_ID128_FORMAT_VAL(machine
)) < 0)
323 fd
= open(p
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
, 0600);
326 log_error_errno(errno
, "Failed to open %s: %m", p
);
332 if (fstat(fd
, &st
) < 0) {
337 if (st
.st_size
< (off_t
) sizeof(FSSHeader
)) {
342 m
= mmap(NULL
, PAGE_ALIGN(sizeof(FSSHeader
)), PROT_READ
, MAP_SHARED
, fd
, 0);
343 if (m
== MAP_FAILED
) {
349 if (memcmp(m
->signature
, FSS_HEADER_SIGNATURE
, 8) != 0) {
354 if (m
->incompatible_flags
!= 0) {
355 r
= -EPROTONOSUPPORT
;
359 if (le64toh(m
->header_size
) < sizeof(FSSHeader
)) {
364 if (le64toh(m
->fsprg_state_size
) != FSPRG_stateinbytes(le16toh(m
->fsprg_secpar
))) {
369 f
->fss_file_size
= le64toh(m
->header_size
) + le64toh(m
->fsprg_state_size
);
370 if ((uint64_t) st
.st_size
< f
->fss_file_size
) {
375 if (!sd_id128_equal(machine
, m
->machine_id
)) {
380 if (le64toh(m
->start_usec
) <= 0 ||
381 le64toh(m
->interval_usec
) <= 0) {
386 f
->fss_file
= mmap(NULL
, PAGE_ALIGN(f
->fss_file_size
), PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
387 if (f
->fss_file
== MAP_FAILED
) {
393 f
->fss_start_usec
= le64toh(f
->fss_file
->start_usec
);
394 f
->fss_interval_usec
= le64toh(f
->fss_file
->interval_usec
);
396 f
->fsprg_state
= (uint8_t*) f
->fss_file
+ le64toh(f
->fss_file
->header_size
);
397 f
->fsprg_state_size
= le64toh(f
->fss_file
->fsprg_state_size
);
403 munmap(m
, PAGE_ALIGN(sizeof(FSSHeader
)));
411 int journal_file_hmac_setup(JournalFile
*f
) {
417 initialize_libgcrypt(true);
419 e
= gcry_md_open(&f
->hmac
, GCRY_MD_SHA256
, GCRY_MD_FLAG_HMAC
);
426 int journal_file_append_first_tag(JournalFile
*f
) {
433 log_debug("Calculating first tag...");
435 r
= journal_file_hmac_put_header(f
);
439 p
= le64toh(f
->header
->field_hash_table_offset
);
440 if (p
< offsetof(Object
, hash_table
.items
))
442 p
-= offsetof(Object
, hash_table
.items
);
444 r
= journal_file_hmac_put_object(f
, OBJECT_FIELD_HASH_TABLE
, NULL
, p
);
448 p
= le64toh(f
->header
->data_hash_table_offset
);
449 if (p
< offsetof(Object
, hash_table
.items
))
451 p
-= offsetof(Object
, hash_table
.items
);
453 r
= journal_file_hmac_put_object(f
, OBJECT_DATA_HASH_TABLE
, NULL
, p
);
457 r
= journal_file_append_tag(f
);
464 int journal_file_parse_verification_key(JournalFile
*f
, const char *key
) {
469 unsigned long long start
, interval
;
471 seed_size
= FSPRG_RECOMMENDED_SEEDLEN
;
472 seed
= malloc(seed_size
);
477 for (c
= 0; c
< seed_size
; c
++) {
496 seed
[c
] = (uint8_t) (x
* 16 + y
);
505 r
= sscanf(k
, "%llx-%llx", &start
, &interval
);
511 f
->fsprg_seed
= seed
;
512 f
->fsprg_seed_size
= seed_size
;
514 f
->fss_start_usec
= start
* interval
;
515 f
->fss_interval_usec
= interval
;
520 bool journal_file_next_evolve_usec(JournalFile
*f
, usec_t
*u
) {
529 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
531 *u
= (usec_t
) (f
->fss_start_usec
+ f
->fss_interval_usec
* epoch
+ f
->fss_interval_usec
);