kiwi-el8/test/unit/storage/integrity_device_test.py
Marcus Schäfer e7e8c3bf42
Add support for dm integrity with secret key
Allow to protect the opening of the integrity data map and
journal through a keyfile. For setting the key file two new
optional type attributes were added:

* integrity_keyfile
* integrity_metadata_key_description

The key file format must be correct according to the selected
integrity algorithm. As of now the kiwi default hmac-sha256
algorithm is used with the selected keyfile

The optional integrity_metadata_key_description attribute
allows to specify a custom description of an integrity key
as it is expected to be present in the kernel keyring. The
information is placed in the integrity metadata block. If
not specified kiwi creates a key argument string instead
which is based on the given integrity_keyfile filename.
The format of this key argument is:

:BASENAME_OF_integrity_keyfile_WITHOUT_FILE_EXTENSION
2022-05-04 16:04:37 +02:00

231 lines
8.8 KiB
Python

import io
from textwrap import dedent
from pytest import raises
from mock import (
patch, Mock, call, MagicMock
)
from kiwi.storage.integrity_device import (
IntegrityDevice,
integrity_credentials_type
)
from kiwi.exceptions import KiwiOffsetError
import kiwi.defaults as defaults
class TestIntegrityDevice:
@patch('os.path.getsize')
def setup(self, mock_os_path_getsize):
mock_os_path_getsize.return_value = 42
metadata_file = Mock()
metadata_file.name = 'metadata_file'
storage_device = Mock()
storage_device.get_device = Mock(
return_value='/dev/some-device'
)
storage_device.is_loop = Mock(
return_value=True
)
self.integrity = IntegrityDevice(
storage_device, defaults.INTEGRITY_ALGORITHM
)
self.integrity_hmac = IntegrityDevice(
storage_device, defaults.INTEGRITY_ALGORITHM,
integrity_credentials_type(
keydescription=None,
keyfile='/etc/pki/storage/dm-integrity-hmac-secret.bin',
keyfile_algorithm=defaults.INTEGRITY_KEY_ALGORITHM
)
)
self.integrity.integrity_metadata_file = metadata_file
@patch('os.path.getsize')
def setup_method(self, cls, mock_os_path_getsize):
self.setup()
@patch('kiwi.storage.integrity_device.MappedDevice')
def test_get_device(self, mock_MappedDevice):
self.integrity.integrity_device = '/dev/some-device'
assert self.integrity.get_device() == mock_MappedDevice.return_value
self.integrity.integrity_device = None
assert self.integrity.get_device() is None
def test_is_loop(self):
assert self.integrity.is_loop() is True
@patch('kiwi.storage.integrity_device.Command.run')
def test_create_dm_integrity(self, mock_Command_run):
self.integrity.create_dm_integrity()
assert mock_Command_run.call_args_list == [
call(
[
'integritysetup', '-v', '--batch-mode', 'format',
'--integrity', 'sha256', '--sector-size', '512',
'/dev/some-device'
]
),
call(
[
'integritysetup', '-v', '--batch-mode', 'open',
'--integrity', 'sha256',
'/dev/some-device', 'integrityRoot'
]
)
]
@patch('kiwi.storage.integrity_device.Command.run')
def test_create_dm_integrity_with_hmac(self, mock_Command_run):
self.integrity_hmac.create_dm_integrity()
assert mock_Command_run.call_args_list == [
call(
[
'integritysetup', '-v', '--batch-mode', 'format',
'--integrity', 'hmac-sha256', '--sector-size', '512',
'--integrity-key-file',
'/etc/pki/storage/dm-integrity-hmac-secret.bin',
'--integrity-key-size', '42',
'/dev/some-device'
]
),
call(
[
'integritysetup', '-v', '--batch-mode', 'open',
'--integrity', 'hmac-sha256',
'--integrity-key-file',
'/etc/pki/storage/dm-integrity-hmac-secret.bin',
'--integrity-key-size', '42',
'/dev/some-device', 'integrityRoot'
]
)
]
@patch('kiwi.storage.integrity_device.Command.run')
@patch('kiwi.storage.integrity_device.BlockID')
def test_create_integrity_metadata(
self, mock_BlockID, mock_Command_run
):
mock_BlockID.return_value.get_filesystem.return_value = 'xfs'
mock_Command_run.return_value.output = dedent('''\n
Info for integrity device /dev/mapper/loop0p4.
superblock_version 4
log2_interleave_sectors 15
integrity_tag_size 32
journal_sections 212
provided_data_sectors 2234096
sector_size 512
log2_blocks_per_bitmap 15
flags fix_padding
''')
with patch('builtins.open', create=True) as mock_open:
mock_open.return_value = MagicMock(spec=io.IOBase)
file_handle = mock_open.return_value.__enter__.return_value
self.integrity.create_integrity_metadata()
assert file_handle.write.call_args_list == [
call(b'1 xfs rw integrity'),
call(b'\xff'),
call(b'2234096 512 2 internal_hash:sha256 fix_padding'),
call(b'\xff'),
call(b'\x00')
]
@patch('kiwi.storage.integrity_device.Command.run')
@patch('kiwi.storage.integrity_device.BlockID')
def test_create_integrity_metadata_with_hmac(
self, mock_BlockID, mock_Command_run
):
mock_BlockID.return_value.get_filesystem.return_value = 'xfs'
mock_Command_run.return_value.output = dedent('''\n
Info for integrity device /dev/mapper/loop0p4.
superblock_version 4
log2_interleave_sectors 15
integrity_tag_size 32
journal_sections 212
provided_data_sectors 2234096
sector_size 512
log2_blocks_per_bitmap 15
flags fix_padding
''')
with patch('builtins.open', create=True) as mock_open:
mock_open.return_value = MagicMock(spec=io.IOBase)
file_handle = mock_open.return_value.__enter__.return_value
self.integrity_hmac.create_integrity_metadata()
assert file_handle.write.call_args_list == [
call(b'1 xfs rw integrity'),
call(b'\xff'),
call(
b'2234096 512 2 internal_hash:'
b'hmac(sha256)::dm-integrity-hmac-secret fix_padding'
),
call(b'\xff'),
call(b'\x00')
]
@patch('kiwi.storage.integrity_device.Signature')
def test_sign_integrity_metadata(self, mock_Signature):
self.integrity.sign_integrity_metadata()
mock_Signature.return_value.sign.assert_called_once_with()
@patch('os.path.getsize')
def test_write_integrity_metadata_raises(self, mock_os_path_getsize):
mock_os_path_getsize.return_value = 8192
with raises(KiwiOffsetError):
self.integrity.write_integrity_metadata()
@patch('os.path.getsize')
def test_write_integrity_metadata(self, mock_os_path_getsize):
mock_os_path_getsize.return_value = 4096
with patch('builtins.open', create=True) as mock_open:
mock_open.return_value = MagicMock(spec=io.IOBase)
file_handle = mock_open.return_value.__enter__.return_value
self.integrity.write_integrity_metadata()
assert mock_open.call_args_list == [
call('metadata_file', 'rb'),
call('/dev/some-device', 'r+b')
]
file_handle.seek.assert_called_once_with(
-defaults.DM_METADATA_OFFSET, 2
)
file_handle.write.assert_called_once_with(
file_handle.read.return_value
)
@patch('kiwi.storage.integrity_device.BlockID')
def test_create_integritytab(self, mock_BlockID):
mock_BlockID.return_value.get_blkid.return_value = 'id'
with patch('builtins.open', create=True) as mock_open:
mock_open.return_value = MagicMock(spec=io.IOBase)
file_handle = mock_open.return_value.__enter__.return_value
self.integrity.create_integritytab('integritytab')
file_handle.write.assert_called_once_with(
'integrityRoot PARTUUID=id - integrity-algorithm=sha256\n'
)
@patch('kiwi.storage.integrity_device.BlockID')
def test_create_integritytab_with_keyfile(self, mock_BlockID):
mock_BlockID.return_value.get_blkid.return_value = 'id'
with patch('builtins.open', create=True) as mock_open:
mock_open.return_value = MagicMock(spec=io.IOBase)
file_handle = mock_open.return_value.__enter__.return_value
self.integrity_hmac.create_integritytab('integritytab')
file_handle.write.assert_called_once_with(
'integrityRoot PARTUUID=id '
'/etc/pki/storage/dm-integrity-hmac-secret.bin '
'integrity-algorithm=hmac-sha256\n'
)
@patch('kiwi.storage.integrity_device.Command.run')
@patch('kiwi.storage.integrity_device.log.warning')
def test_destructor(self, mock_log_warn, mock_Command_run):
self.integrity.integrity_device = '/dev/mapper/integrityRoot'
mock_Command_run.side_effect = Exception
self.integrity.__del__()
mock_Command_run.assert_called_once_with(
['integritysetup', 'close', 'integrityRoot']
)
assert mock_log_warn.called
self.integrity.integrity_device = None