Merge "Add LUKSv1 inspector"

This commit is contained in:
Zuul 2024-10-25 16:30:29 +00:00 committed by Gerrit Code Review
commit 7ab82d4a60
2 changed files with 64 additions and 3 deletions

View File

@ -1276,6 +1276,45 @@ class GPTInspector(FileInspector):
raise SafetyViolation('GPT MBR has no partitions defined')
# The LUKSv1 format consists of a header with some metadata and key
# information followed by a bulk non-sparse data payload which is the
# encyrpted disk image.
# https://gitlab.com/cryptsetup/cryptsetup/-/wikis/LUKS-standard/on-disk-format.pdf
# LUKSv2 is a different but similar spec, which is not yet covered here (or
# in qemu).
class LUKSInspector(FileInspector):
NAME = 'luks'
def _initialize(self):
self.new_region('header', CaptureRegion(0, 592))
self.add_safety_check(SafetyCheck('version', self.check_version))
@property
def format_match(self):
return self.region('header').data[:6] == b'LUKS\xBA\xBE'
@property
def header_items(self):
fields = struct.unpack('>6sh32s32s32sI',
self.region('header').data[:108])
names = ['magic', 'version', 'cipher_alg', 'cipher_mode', 'hash',
'payload_offset']
return dict(zip(names, fields))
def check_version(self):
header = self.header_items
if header['version'] != 1:
raise SafetyViolation(
'LUKS version %i is not supported' % header['version'])
@property
def virtual_size(self):
# NOTE(danms): This will not be correct until/unless the whole stream
# has been read, since all we have is (effectively the size of the
# header. This is similar to how RawFileInspector works.
return super().virtual_size - self.header_items['payload_offset'] * 512
class InspectWrapper:
"""A file-like object that wraps another and detects the format.
@ -1437,6 +1476,7 @@ ALL_FORMATS = {
'qed': QEDInspector,
'iso': ISOInspector,
'gpt': GPTInspector,
'luks': LUKSInspector,
}

View File

@ -127,6 +127,14 @@ class TestFormatInspectors(test_base.BaseTestCase):
self._created_files.append(fn)
return fn
def _create_luks(self, image_size, subformat):
fn = tempfile.mktemp(suffix='.luks')
cmd = ['qemu-img', 'create', '-f', 'luks',
'--object', 'secret,id=sec0,data=secret-passphrase',
'-o', 'key-secret=sec0', fn, '%i' % image_size]
subprocess.check_output(' '.join(cmd), shell=True)
return fn
def _create_img(
self, fmt, size, subformat=None, options=None,
backing_file=None):
@ -143,6 +151,8 @@ class TestFormatInspectors(test_base.BaseTestCase):
return self._create_iso(size, subformat)
if fmt == 'gpt':
return self._create_gpt(size, subformat)
if fmt == 'luks':
return self._create_luks(size, subformat)
if fmt == 'vhd':
# QEMU calls the vhd format vpc
@ -216,11 +226,22 @@ class TestFormatInspectors(test_base.BaseTestCase):
def _test_format_at_block_size(self, format_name, img, block_size):
wrapper = format_inspector.InspectWrapper(open(img, 'rb'),
format_name)
current_block_size = block_size
while True:
chunk = wrapper.read(block_size)
chunk = wrapper.read(current_block_size)
if not chunk:
break
# If we've already settled on a format, the block size no longer
# really matters for correctness since we won't be capturing and
# parsing anything else. Bump up the block size so we will eat
# the rest of the file more efficiently. This matters for formats
# that are non-sparse and for which the virtual_size calculation
# relies on the actual size of the file (i.e. raw, gpt, luks, etc)
try:
if current_block_size == block_size and wrapper.format:
current_block_size = 64 * units.Ki
except Exception:
pass
wrapper.close()
self.assertIsNotNone(wrapper.format, 'Failed to detect format')
@ -279,7 +300,7 @@ class TestFormatInspectors(test_base.BaseTestCase):
subformat=subformat,
safety_check=True)
@ddt.data('qcow2', 'vhd', 'vhdx', 'vmdk', 'gpt')
@ddt.data('qcow2', 'vhd', 'vhdx', 'vmdk', 'gpt', 'luks')
def test_format(self, format):
self._test_format(format)