diff --git a/man/crypttab.xml b/man/crypttab.xml
index dbbeb3fd5b..f0d0ead947 100644
--- a/man/crypttab.xml
+++ b/man/crypttab.xml
@@ -1023,6 +1023,11 @@
if it has a different one. The expected hash matches the digest which is measured to the sha256 PCR bank of the
TPM2 when is used.
+ For newly created LUKS volumes, the expected hash can be generated by
+ systemd-repart8.
+ For additional details, see the description at
+ repart.d5.
+
@@ -1136,6 +1141,8 @@ external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac
systemd-cryptsetup@.service8systemd-cryptsetup-generator8systemd-cryptenroll1
+ systemd-repart8
+ repart.d5fstab5cryptsetup8mkswap8
diff --git a/man/repart.d.xml b/man/repart.d.xml
index 5d32557745..5cdf2b0f34 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -869,13 +869,18 @@
EncryptedVolume=Specifies how the encrypted partition should be set up. Takes at least one and at most
- three fields separated with a colon (:). The first field specifies the encrypted
+ four fields separated with a colon (:). The first field specifies the encrypted
volume name under /dev/mapper/. If not specified, luks-UUID
will be used where UUID is the LUKS UUID. The second field specifies the keyfile
to use following the same format as specified in crypttab. The third field specifies a
- comma-delimited list of crypttab options. These fields correspond to the first, third and fourth
+ comma-delimited list of crypttab options. These three fields correspond to the first, third and fourth
column of the
crypttab5 format.
+ The fourth field contains extra options, which are not directly reflected to crypttab options. Currently,
+ the only supported extra option is fixate-volume-key. When specified, the volume key
+ hash will be taken upon LUKS volume formatting and the result will be added to the list of crypttab options,
+ i.e. fixate-volume-key=expected-hash will be added to the fourth column of the generated
+ crypttab.
Note that this setting is only taken into account when
diff --git a/src/repart/repart.c b/src/repart/repart.c
index 7c19f10287..48e05d3769 100644
--- a/src/repart/repart.c
+++ b/src/repart/repart.c
@@ -318,6 +318,7 @@ typedef struct PartitionEncryptedVolume {
char *name;
char *keyfile;
char *options;
+ bool fixate_volume_key;
} PartitionEncryptedVolume;
static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryptedVolume *c) {
@@ -2547,7 +2548,8 @@ static int config_parse_encrypted_volume(
void *data,
void *userdata) {
- _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL;
+ _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL, *extra = NULL;
+ bool fixate_volume_key = false;
Partition *p = ASSERT_PTR(data);
int r;
@@ -2558,7 +2560,7 @@ static int config_parse_encrypted_volume(
const char *q = rvalue;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE,
- &volume, &keyfile, &options);
+ &volume, &keyfile, &options, &extra);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
@@ -2589,10 +2591,29 @@ static int config_parse_encrypted_volume(
if (!p->encrypted_volume)
return log_oom();
+ for (const char *e = extra;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&e, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to parse extra options '%s', ignoring", word);
+ break;
+ }
+ if (r == 0)
+ break;
+
+ if (streq(word, "fixate-volume-key"))
+ fixate_volume_key = true;
+ else
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown extra option '%s', ignoring", word);
+ }
+
*p->encrypted_volume = (PartitionEncryptedVolume) {
.name = TAKE_PTR(volume),
.keyfile = TAKE_PTR(keyfile),
.options = TAKE_PTR(options),
+ .fixate_volume_key = fixate_volume_key,
};
return 0;
@@ -5174,6 +5195,44 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
if (r < 0)
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
+ if (p->encrypted_volume && p->encrypted_volume->fixate_volume_key) {
+ _cleanup_free_ char *key_id = NULL, *hash_option = NULL;
+
+ r = sym_crypt_get_volume_key_size(cd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine volume key size: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume key has zero size and 'fixate-volume-key' is used");
+
+ _cleanup_(iovec_done) struct iovec vk = {
+ .iov_base = malloc(r),
+ .iov_len = r,
+ };
+
+ if (!vk.iov_base)
+ return log_oom();
+
+ r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) vk.iov_base, &vk.iov_len, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get volume key: %m");
+
+ r = cryptsetup_get_volume_key_id(
+ cd,
+ /* volume_name= */ p->encrypted_volume->name,
+ /* volume_key= */ vk.iov_base,
+ /* volume_key_size= */ vk.iov_len,
+ /* ret= */ &key_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get volume key hash: %m");
+
+ hash_option = strjoin("fixate-volume-key=", key_id);
+ if (!hash_option)
+ return log_oom();
+
+ if (!strextend_with_separator(&p->encrypted_volume->options, ",", hash_option))
+ return log_oom();
+ }
+
if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
/* Use partition-specific key if available, otherwise fall back to global key */
struct iovec *iovec_key = arg_key.iov_base ? &arg_key : &p->key;