Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/disk/encryption_menu.py
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib/disk/encryption_menu.py')
-rw-r--r--archinstall/lib/disk/encryption_menu.py288
1 files changed, 288 insertions, 0 deletions
diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py
new file mode 100644
index 00000000..b0e292ce
--- /dev/null
+++ b/archinstall/lib/disk/encryption_menu.py
@@ -0,0 +1,288 @@
+from pathlib import Path
+from typing import Dict, Optional, Any, TYPE_CHECKING, List
+
+from . import LvmConfiguration, LvmVolume
+from ..disk import (
+ DeviceModification,
+ DiskLayoutConfiguration,
+ PartitionModification,
+ DiskEncryption,
+ EncryptionType
+)
+from ..menu import (
+ Selector,
+ AbstractSubMenu,
+ MenuSelectionType,
+ TableMenu
+)
+from ..interactions.utils import get_password
+from ..menu import Menu
+from ..general import secret
+from .fido import Fido2Device, Fido2
+from ..output import FormattedOutput
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class DiskEncryptionMenu(AbstractSubMenu):
+ def __init__(
+ self,
+ disk_config: DiskLayoutConfiguration,
+ data_store: Dict[str, Any],
+ preset: Optional[DiskEncryption] = None
+ ):
+ if preset:
+ self._preset = preset
+ else:
+ self._preset = DiskEncryption()
+
+ self._disk_config = disk_config
+ super().__init__(data_store=data_store)
+
+ def setup_selection_menu_options(self):
+ self._menu_options['encryption_type'] = \
+ Selector(
+ _('Encryption type'),
+ func=lambda preset: select_encryption_type(self._disk_config, preset),
+ display_func=lambda x: EncryptionType.type_to_text(x) if x else None,
+ default=self._preset.encryption_type,
+ enabled=True,
+ )
+ self._menu_options['encryption_password'] = \
+ Selector(
+ _('Encryption password'),
+ lambda x: select_encrypted_password(),
+ dependencies=[self._check_dep_enc_type],
+ display_func=lambda x: secret(x) if x else '',
+ default=self._preset.encryption_password,
+ enabled=True
+ )
+ self._menu_options['partitions'] = \
+ Selector(
+ _('Partitions'),
+ func=lambda preset: select_partitions_to_encrypt(self._disk_config.device_modifications, preset),
+ display_func=lambda x: f'{len(x)} {_("Partitions")}' if x else None,
+ dependencies=[self._check_dep_partitions],
+ default=self._preset.partitions,
+ preview_func=self._prev_partitions,
+ enabled=True
+ )
+ self._menu_options['lvm_vols'] = \
+ Selector(
+ _('LVM volumes'),
+ func=lambda preset: self._select_lvm_vols(preset),
+ display_func=lambda x: f'{len(x)} {_("LVM volumes")}' if x else None,
+ dependencies=[self._check_dep_lvm_vols],
+ default=self._preset.lvm_volumes,
+ preview_func=self._prev_lvm_vols,
+ enabled=True
+ )
+ self._menu_options['HSM'] = \
+ Selector(
+ description=_('Use HSM to unlock encrypted drive'),
+ func=lambda preset: select_hsm(preset),
+ display_func=lambda x: self._display_hsm(x),
+ preview_func=self._prev_hsm,
+ dependencies=[self._check_dep_enc_type],
+ default=self._preset.hsm_device,
+ enabled=True
+ )
+
+ def _select_lvm_vols(self, preset: List[LvmVolume]) -> List[LvmVolume]:
+ if self._disk_config.lvm_config:
+ return select_lvm_vols_to_encrypt(self._disk_config.lvm_config, preset=preset)
+ return []
+
+ def _check_dep_enc_type(self) -> bool:
+ enc_type: Optional[EncryptionType] = self._menu_options['encryption_type'].current_selection
+ if enc_type and enc_type != EncryptionType.NoEncryption:
+ return True
+ return False
+
+ def _check_dep_partitions(self) -> bool:
+ enc_type: Optional[EncryptionType] = self._menu_options['encryption_type'].current_selection
+ if enc_type and enc_type in [EncryptionType.Luks, EncryptionType.LvmOnLuks]:
+ return True
+ return False
+
+ def _check_dep_lvm_vols(self) -> bool:
+ enc_type: Optional[EncryptionType] = self._menu_options['encryption_type'].current_selection
+ if enc_type and enc_type == EncryptionType.LuksOnLvm:
+ return True
+ return False
+
+ def run(self, allow_reset: bool = True) -> Optional[DiskEncryption]:
+ super().run(allow_reset=allow_reset)
+
+ enc_type = self._data_store.get('encryption_type', None)
+ enc_password = self._data_store.get('encryption_password', None)
+ enc_partitions = self._data_store.get('partitions', None)
+ enc_lvm_vols = self._data_store.get('lvm_vols', None)
+
+ if enc_type in [EncryptionType.Luks, EncryptionType.LvmOnLuks] and enc_partitions:
+ enc_lvm_vols = []
+
+ if enc_type == EncryptionType.LuksOnLvm:
+ enc_partitions = []
+
+ if enc_type != EncryptionType.NoEncryption and enc_password and (enc_partitions or enc_lvm_vols):
+ return DiskEncryption(
+ encryption_password=enc_password,
+ encryption_type=enc_type,
+ partitions=enc_partitions,
+ lvm_volumes=enc_lvm_vols,
+ hsm_device=self._data_store.get('HSM', None)
+ )
+
+ return None
+
+ def _display_hsm(self, device: Optional[Fido2Device]) -> Optional[str]:
+ if device:
+ return device.manufacturer
+
+ return None
+
+ def _prev_partitions(self) -> Optional[str]:
+ partitions: Optional[List[PartitionModification]] = self._menu_options['partitions'].current_selection
+ if partitions:
+ output = str(_('Partitions to be encrypted')) + '\n'
+ output += FormattedOutput.as_table(partitions)
+ return output.rstrip()
+
+ return None
+
+ def _prev_lvm_vols(self) -> Optional[str]:
+ volumes: Optional[List[PartitionModification]] = self._menu_options['lvm_vols'].current_selection
+ if volumes:
+ output = str(_('LVM volumes to be encrypted')) + '\n'
+ output += FormattedOutput.as_table(volumes)
+ return output.rstrip()
+
+ return None
+
+ def _prev_hsm(self) -> Optional[str]:
+ try:
+ Fido2.get_fido2_devices()
+ except ValueError:
+ return str(_('Unable to determine fido2 devices. Is libfido2 installed?'))
+
+ fido_device: Optional[Fido2Device] = self._menu_options['HSM'].current_selection
+
+ if fido_device:
+ output = '{}: {}'.format(str(_('Path')), fido_device.path)
+ output += '{}: {}'.format(str(_('Manufacturer')), fido_device.manufacturer)
+ output += '{}: {}'.format(str(_('Product')), fido_device.product)
+ return output
+
+ return None
+
+
+def select_encryption_type(disk_config: DiskLayoutConfiguration, preset: EncryptionType) -> Optional[EncryptionType]:
+ title = str(_('Select disk encryption option'))
+
+ if disk_config.lvm_config:
+ options = [
+ EncryptionType.type_to_text(EncryptionType.LvmOnLuks),
+ EncryptionType.type_to_text(EncryptionType.LuksOnLvm)
+ ]
+ else:
+ options = [EncryptionType.type_to_text(EncryptionType.Luks)]
+
+ preset_value = EncryptionType.type_to_text(preset)
+
+ choice = Menu(title, options, preset_values=preset_value).run()
+
+ match choice.type_:
+ case MenuSelectionType.Reset: return None
+ case MenuSelectionType.Skip: return preset
+ case MenuSelectionType.Selection: return EncryptionType.text_to_type(choice.value) # type: ignore
+
+
+def select_encrypted_password() -> Optional[str]:
+ if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
+ return passwd
+ return None
+
+
+def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]:
+ title = _('Select a FIDO2 device to use for HSM')
+
+ try:
+ fido_devices = Fido2.get_fido2_devices()
+ except ValueError:
+ return None
+
+ if fido_devices:
+ choice = TableMenu(title, data=fido_devices).run()
+ match choice.type_:
+ case MenuSelectionType.Reset:
+ return None
+ case MenuSelectionType.Skip:
+ return preset
+ case MenuSelectionType.Selection:
+ return choice.value # type: ignore
+
+ return None
+
+
+def select_partitions_to_encrypt(
+ modification: List[DeviceModification],
+ preset: List[PartitionModification]
+) -> List[PartitionModification]:
+ partitions: List[PartitionModification] = []
+
+ # do not allow encrypting the boot partition
+ for mod in modification:
+ partitions += list(filter(lambda x: x.mountpoint != Path('/boot'), mod.partitions))
+
+ # do not allow encrypting existing partitions that are not marked as wipe
+ avail_partitions = list(filter(lambda x: not x.exists(), partitions))
+
+ if avail_partitions:
+ title = str(_('Select which partitions to encrypt'))
+ partition_table = FormattedOutput.as_table(avail_partitions)
+
+ choice = TableMenu(
+ title,
+ table_data=(avail_partitions, partition_table),
+ preset=preset,
+ multi=True
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Reset:
+ return []
+ case MenuSelectionType.Skip:
+ return preset
+ case MenuSelectionType.Selection:
+ return choice.multi_value
+ return []
+
+
+def select_lvm_vols_to_encrypt(
+ lvm_config: LvmConfiguration,
+ preset: List[LvmVolume]
+) -> List[LvmVolume]:
+ volumes: List[LvmVolume] = lvm_config.get_all_volumes()
+
+ if volumes:
+ title = str(_('Select which LVM volumes to encrypt'))
+ partition_table = FormattedOutput.as_table(volumes)
+
+ choice = TableMenu(
+ title,
+ table_data=(volumes, partition_table),
+ preset=preset,
+ multi=True
+ ).run()
+
+ match choice.type_:
+ case MenuSelectionType.Reset:
+ return []
+ case MenuSelectionType.Skip:
+ return preset
+ case MenuSelectionType.Selection:
+ return choice.multi_value
+
+ return []