diff --git a/man/repart.d.xml b/man/repart.d.xml
index 8de0435735..eaa83f6171 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -709,6 +709,10 @@
This option has no effect if the partition already exists.
+ This option may be combined with Verity=data in order to place the
+ encrypted partition inside a dm-verity protection envelope, see the description of
+ Verity= below.
+
@@ -773,7 +777,19 @@
This option has no effect if the partition already exists.
- Usage of this option in combination with Encrypt= is not supported.
+ data may be combined with Encrypt=, in which case the
+ partition is first encrypted with LUKS2 and the verity hash data is then generated from the
+ resulting ciphertext, i.e. Verity is the outer protection envelope and LUKS2 the inner. Tools such
+ as systemd-dissect1
+ will then set up the dm-verity device first, and open the LUKS2 volume on top of it, so that all
+ encrypted data read is also authenticated via the verity root hash. Note that in this case the
+ verity hashes necessarily cover freshly generated ciphertext, hence the root hash (and thus the
+ default partition UUIDs derived from it) will change on every build, even if the plaintext contents
+ are fully reproducible. Encrypt= may not be combined with
+ Verity=hash or Verity=signature. Since the ciphertext is only
+ generated while the final image is built, Minimize= cannot be used for the hash
+ partition of an encrypted data partition; use SizeMaxBytes= on the data partition
+ to size the hash partition instead.
For each unique VerityMatchKey= value, a single verity data partition
(Verity=data) and a single verity hash partition (Verity=hash)
diff --git a/src/repart/repart.c b/src/repart/repart.c
index 002fb782be..645d22ea64 100644
--- a/src/repart/repart.c
+++ b/src/repart/repart.c
@@ -3138,9 +3138,9 @@ static int partition_read_definition(
verity_mode_to_string(p->verity));
}
- if (p->verity != VERITY_OFF && p->encrypt != ENCRYPT_OFF)
+ if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG) && p->encrypt != ENCRYPT_OFF)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
- "Encrypting verity hash/data partitions is not supported.");
+ "Encrypting verity hash/signature partitions is not supported.");
if (p->verity == VERITY_SIG && (p->size_min != UINT64_MAX || p->size_max != UINT64_MAX))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
@@ -3667,6 +3667,13 @@ static int context_read_definitions(Context *context) {
if (dp->minimize == MINIMIZE_OFF && !(dp->copy_blocks_path || dp->copy_blocks_auto))
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Minimize= set for verity hash partition but data partition does not set CopyBlocks= or Minimize=.");
+
+ /* The verity hash of an encrypted data partition covers the ciphertext, which is generated
+ * with a fresh volume key only when the final image is built, hence it cannot be
+ * precalculated for minimizing purposes. */
+ if (dp->encrypt != ENCRYPT_OFF)
+ return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "Minimize= cannot be set for verity hash partitions whose data partition is encrypted, use SizeMaxBytes= on the data partition instead.");
}
LIST_FOREACH(partitions, p, context->partitions) {
@@ -5323,6 +5330,15 @@ static int partition_target_prepare(
return 0;
}
+static void partition_target_drop_decrypted(PartitionTarget *t) {
+ assert(t);
+
+ /* Deactivate the dm-crypt device again, so that subsequent access to the target reaches the
+ * encrypted data as it is stored on disk (i.e. the ciphertext). Requires the target to have been
+ * sync'ed first. */
+ t->decrypted = decrypted_partition_target_free(t->decrypted);
+}
+
static int partition_target_grow(PartitionTarget *t, uint64_t size) {
int r;
@@ -6411,6 +6427,11 @@ static int context_copy_blocks(Context *context) {
p->partno, FORMAT_TIMESPAN(time_spent, 0));
if (p->siblings[VERITY_HASH] && !partition_defer(context, p->siblings[VERITY_HASH])) {
+ /* The verity hash must cover the partition contents as stored on disk, i.e. the
+ * ciphertext if the partition is encrypted, hence tear down the dm-crypt device
+ * first. */
+ partition_target_drop_decrypted(t);
+
r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
/* node= */ NULL, partition_target_path(t));
if (r < 0)
@@ -7600,6 +7621,11 @@ static int context_mkfs(Context *context) {
return r;
if (p->siblings[VERITY_HASH] && !partition_defer(context, p->siblings[VERITY_HASH])) {
+ /* The verity hash must cover the partition contents as stored on disk, i.e. the
+ * ciphertext if the partition is encrypted, hence tear down the dm-crypt device
+ * first. */
+ partition_target_drop_decrypted(t);
+
r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
/* node= */ NULL, partition_target_path(t));
if (r < 0)