Skip to content

Commit 4b908c7

Browse files
committed
move integrity detection to header validation, add tests
Signed-off-by: Zen <[email protected]>
1 parent 20360e1 commit 4b908c7

File tree

4 files changed

+43
-13
lines changed

4 files changed

+43
-13
lines changed

src/ugrd/crypto/cryptsetup.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from ugrd.exceptions import AutodetectError, ValidationError
1010
from zenlib.util import colorize as c_
11-
from zenlib.util import contains, unset, pretty_print
11+
from zenlib.util import contains, unset
1212

1313
_module_name = "ugrd.crypto.cryptsetup"
1414

@@ -151,11 +151,6 @@ def _get_dm_slave_info(self, device_info: dict) -> (str, dict):
151151
# For integrity backed devices, get the slave's slave
152152
if self["_vblk_info"].get(slave_source, {}).get("uuid", "").startswith("CRYPT-SUBDEV"):
153153
slave_source = self["_vblk_info"][slave_source]["slaves"][0]
154-
# Set the _dm-integrity flag on the cryptsetup configuration, if it exists
155-
if device_info.get("name") in self["cryptsetup"]:
156-
self["cryptsetup"][device_info["name"]]["_dm-integrity"] = True
157-
else:
158-
self.logger.warning(f"[{c_(device_info['name'], 'yellow')}] Unable to set _dm-integrity flag, cryptsetup configuration not found: {pretty_print(self['cryptsetup'])}")
159154
slave_name = self["_vblk_info"].get(slave_source, {}).get("name")
160155
search_paths = ["/dev/", "/dev/mapper/"]
161156

@@ -238,6 +233,16 @@ def _detect_luks_header_sha(self, luks_info: dict) -> dict:
238233
self["kernel_modules"] = self._crypto_ciphers[digest["hash"]]["driver"]
239234

240235

236+
def _detect_luks_header_integrity(self, luks_info: dict) -> dict:
237+
""" Reads the integrity algorithm from the LUKS header,
238+
Enables the dm-integrity module, and returns the integrity type. """
239+
for segment in luks_info.get("segments", {}).values():
240+
if integrity_type := segment.get("integrity", {}).get("type"):
241+
self["kernel_modules"] = "dm_integrity"
242+
return integrity_type
243+
244+
245+
241246
@contains("cryptsetup_header_validation", "Skipping cryptsetup header validation.", log_level=30)
242247
def _validate_cryptsetup_header(self, mapped_name: str) -> None:
243248
"""Validates configured cryptsetup volumes against the LUKS header."""
@@ -274,6 +279,10 @@ def _validate_cryptsetup_header(self, mapped_name: str) -> None:
274279

275280
_detect_luks_header_aes(self, luks_info)
276281
_detect_luks_header_sha(self, luks_info)
282+
if integrity_type := _detect_luks_header_integrity(self, luks_info):
283+
self.logger.info(f"[{c_(mapped_name, 'blue')}] Detected dm-integrity type: {c_(integrity_type, 'cyan')}")
284+
self["cryptsetup"][mapped_name]["_dm-integrity"] = integrity_type
285+
277286

278287
if not self["argon2"]: # if argon support was not detected, check if the header wants it
279288
for keyslot in luks_info.get("keyslots", {}).values():

src/ugrd/fs/mounts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__author__ = "desultory"
2-
__version__ = "7.2.0"
2+
__version__ = "7.2.1"
33

44
from pathlib import Path
55
from re import search
@@ -562,7 +562,7 @@ def _autodetect_dm(self, mountpoint, device=None) -> None:
562562
self.logger.info(f"[{c_(dev_name, 'blue')}] Slave is a CRYPT-SUBDEV, using its slave instead: {c_(slave_source, 'cyan')}")
563563
# Add the kmod for it
564564
self.logger.info(f"[{c_(dev_name, 'blue')}] Adding kmod for CRYPT-SUBDEV: {c_('dm-crypt', 'magenta')}")
565-
self["_kmod_auto"] = "dm_integrity"
565+
self["kernel_modules"] = "dm_integrity"
566566

567567
autodetect_mount_kmods(self, slave_source)
568568

src/ugrd/fs/test_image.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
__version__ = "2.0.0"
1+
__version__ = "2.1.0"
22

3+
from re import match
34
from tempfile import TemporaryDirectory
45

56
from zenlib.util import colorize as c_
67
from zenlib.util import contains
78

8-
99
MIN_FS_SIZES = {"btrfs": 110, "f2fs": 50}
1010

11+
1112
@contains("test_flag", "A test flag must be set to create a test image", raise_exception=True)
1213
def init_banner(self):
1314
"""Initialize the test image banner, set a random flag if not set."""
@@ -33,12 +34,13 @@ def _allocate_image(self, image_path, padding=0):
3334

3435
with open(image_path, "wb") as f:
3536
total_size = (self.test_image_size + padding) * (2**20) # Convert MB to bytes
36-
self.logger.info(f"Allocating {self.test_image_size + padding}MB test image file: { c_(f.name, 'green')}")
37-
self.logger.debug(f"[{f.name}] Total bytes: { c_(total_size, 'green')}")
37+
self.logger.info(f"Allocating {self.test_image_size + padding}MB test image file: {c_(f.name, 'green')}")
38+
self.logger.debug(f"[{f.name}] Total bytes: {c_(total_size, 'green')}")
3839
f.write(b"\0" * total_size)
3940

41+
4042
def _copy_fs_contents(self, image_path, build_dir):
41-
""" Mount and copy the filesystem contents into the image,
43+
"""Mount and copy the filesystem contents into the image,
4244
for filesystems which cannot be created directly from a directory"""
4345
try:
4446
with TemporaryDirectory() as tmp_dir:
@@ -84,6 +86,14 @@ def make_test_luks_image(self, image_path):
8486
keyfile_path = _get_luks_keyfile(self)
8587
self.logger.info("Using LUKS keyfile: %s" % c_(keyfile_path, "green"))
8688
self.logger.info("Creating LUKS image: %s" % c_(image_path, "green"))
89+
extra_args = []
90+
if integrity_type := _get_luks_config(self).get("_dm-integrity"):
91+
# If it's the type reported in the header like <type>(<algo>), turn it into <type>-<algo> for arg usage
92+
if m := match(r"^(?P<type>\w+)\((?P<algo>[\w-]+)\)$", integrity_type):
93+
integrity_type = f"{m['type']}-{m['algo']}"
94+
self.logger.info(f"[{c_(image_path, 'green')}] LUKS integrity type: {c_(integrity_type, 'cyan')}")
95+
extra_args.extend(["--integrity", integrity_type])
96+
8797
self._run(
8898
[
8999
"cryptsetup",
@@ -94,6 +104,7 @@ def make_test_luks_image(self, image_path):
94104
"--batch-mode",
95105
"--key-file",
96106
keyfile_path,
107+
*extra_args,
97108
]
98109
)
99110
self.logger.info("Opening LUKS image: %s" % c_(image_path, "magenta"))

tests/test_cryptsetup.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ def test_cryptsetup_included_key(self):
3131
generator = InitramfsGenerator(logger=self.logger, config="tests/cryptsetup_included_key.toml")
3232
generator.build()
3333

34+
def test_cryptsetup_integrity(self):
35+
"""Tests LUKS based roots using a keyfile included in the initramfs with integrity protection"""
36+
generator = InitramfsGenerator(
37+
logger=self.logger,
38+
config="tests/cryptsetup_included_key.toml",
39+
kernel_modules=["dm-integrity"], # Specify this because its usually auto-detected during header validation
40+
cryptsetup={"root": {"_dm-integrity": "hmac(sha256)"}}, # Use the type like defined in the header, not the proper args to test processing
41+
)
42+
generator.build()
43+
3444

3545
if __name__ == "__main__":
3646
main()

0 commit comments

Comments
 (0)