new ciphers
This commit is contained in:
parent
f8a202d843
commit
ed7ba45ae3
|
|
@ -23,6 +23,7 @@ from archinstall.lib.models.device import (
|
||||||
BtrfsMountOption,
|
BtrfsMountOption,
|
||||||
DeviceModification,
|
DeviceModification,
|
||||||
DiskEncryption,
|
DiskEncryption,
|
||||||
|
EncryptionCipher,
|
||||||
FilesystemType,
|
FilesystemType,
|
||||||
LsblkInfo,
|
LsblkInfo,
|
||||||
ModificationStatus,
|
ModificationStatus,
|
||||||
|
|
@ -281,7 +282,7 @@ class DeviceHandler:
|
||||||
enc_password: Password | None,
|
enc_password: Password | None,
|
||||||
lock_after_create: bool = True,
|
lock_after_create: bool = True,
|
||||||
iter_time: int = DEFAULT_ITER_TIME,
|
iter_time: int = DEFAULT_ITER_TIME,
|
||||||
cipher: str = DEFAULT_CIPHER,
|
cipher: EncryptionCipher = DEFAULT_CIPHER,
|
||||||
) -> Luks2:
|
) -> Luks2:
|
||||||
luks_handler = Luks2(
|
luks_handler = Luks2(
|
||||||
dev_path,
|
dev_path,
|
||||||
|
|
@ -627,4 +628,4 @@ class DeviceHandler:
|
||||||
self._wipe(block_device.device_info.path)
|
self._wipe(block_device.device_info.path)
|
||||||
|
|
||||||
|
|
||||||
device_handler = DeviceHandler()
|
device_handler = DeviceHandler()
|
||||||
|
|
@ -11,6 +11,7 @@ from archinstall.lib.models.device import (
|
||||||
DEFAULT_ITER_TIME,
|
DEFAULT_ITER_TIME,
|
||||||
DeviceModification,
|
DeviceModification,
|
||||||
DiskEncryption,
|
DiskEncryption,
|
||||||
|
EncryptionCipher,
|
||||||
EncryptionType,
|
EncryptionType,
|
||||||
Fido2Device,
|
Fido2Device,
|
||||||
LvmConfiguration,
|
LvmConfiguration,
|
||||||
|
|
@ -112,7 +113,7 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
||||||
return await select_lvm_vols_to_encrypt(self._lvm_config, preset=preset)
|
return await select_lvm_vols_to_encrypt(self._lvm_config, preset=preset)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def _select_cipher(self, preset: str | None) -> str | None:
|
async def _select_cipher(self, preset: EncryptionCipher | None) -> EncryptionCipher | None:
|
||||||
return await select_encryption_cipher(preset)
|
return await select_encryption_cipher(preset)
|
||||||
|
|
||||||
def _check_dep_enc_type(self) -> bool:
|
def _check_dep_enc_type(self) -> bool:
|
||||||
|
|
@ -141,7 +142,7 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
||||||
|
|
||||||
enc_type: EncryptionType | None = self._item_group.find_by_key('encryption_type').value
|
enc_type: EncryptionType | None = self._item_group.find_by_key('encryption_type').value
|
||||||
enc_password: Password | None = self._item_group.find_by_key('encryption_password').value
|
enc_password: Password | None = self._item_group.find_by_key('encryption_password').value
|
||||||
cipher: str | None = self._item_group.find_by_key('cipher').value
|
cipher: EncryptionCipher | None = self._item_group.find_by_key('cipher').value
|
||||||
iter_time: int | None = self._item_group.find_by_key('iter_time').value
|
iter_time: int | None = self._item_group.find_by_key('iter_time').value
|
||||||
enc_partitions = self._item_group.find_by_key('partitions').value
|
enc_partitions = self._item_group.find_by_key('partitions').value
|
||||||
enc_lvm_vols = self._item_group.find_by_key('lvm_volumes').value
|
enc_lvm_vols = self._item_group.find_by_key('lvm_volumes').value
|
||||||
|
|
@ -214,9 +215,9 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _prev_cipher(self, item: MenuItem) -> str | None:
|
def _prev_cipher(self, item: MenuItem) -> str | None:
|
||||||
cipher = self._item_group.find_by_key('cipher').value
|
cipher: EncryptionCipher | None = self._item_group.find_by_key('cipher').value
|
||||||
if cipher:
|
if cipher:
|
||||||
return f'{tr("Encryption cipher")}: {cipher}'
|
return f'{tr("Encryption cipher")}: {cipher.value}'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _prev_partitions(self, item: MenuItem) -> str | None:
|
def _prev_partitions(self, item: MenuItem) -> str | None:
|
||||||
|
|
@ -429,23 +430,17 @@ async def select_iteration_time(preset: int | None = None) -> int | None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def select_encryption_cipher(preset: str | None = None) -> str | None:
|
async def select_encryption_cipher(preset: EncryptionCipher | None = None) -> EncryptionCipher | None:
|
||||||
# Regular block-cipher modes: accepted directly by `cryptsetup --cipher`.
|
options = list(EncryptionCipher)
|
||||||
options = [
|
|
||||||
'aes-xts-plain64',
|
|
||||||
'aes-cbc-essiv:sha256',
|
|
||||||
'serpent-xts-plain64',
|
|
||||||
'twofish-xts-plain64',
|
|
||||||
]
|
|
||||||
|
|
||||||
if not preset:
|
if not preset:
|
||||||
preset = options[0]
|
preset = DEFAULT_CIPHER
|
||||||
|
|
||||||
items = [MenuItem(o, value=o) for o in options]
|
items = [MenuItem(o.value, value=o) for o in options]
|
||||||
group = MenuItemGroup(items)
|
group = MenuItemGroup(items)
|
||||||
group.set_focus_by_value(preset)
|
group.set_focus_by_value(preset)
|
||||||
|
|
||||||
result = await Selection[str](
|
result = await Selection[EncryptionCipher](
|
||||||
group,
|
group,
|
||||||
header=tr('Select encryption cipher'),
|
header=tr('Select encryption cipher'),
|
||||||
allow_skip=True,
|
allow_skip=True,
|
||||||
|
|
@ -458,4 +453,4 @@ async def select_encryption_cipher(preset: str | None = None) -> str | None:
|
||||||
case ResultType.Skip:
|
case ResultType.Skip:
|
||||||
return preset
|
return preset
|
||||||
case ResultType.Selection:
|
case ResultType.Selection:
|
||||||
return result.get_value()
|
return result.get_value()
|
||||||
|
|
@ -8,7 +8,7 @@ from archinstall.lib.command import SysCommand, SysCommandWorker, run
|
||||||
from archinstall.lib.disk.utils import get_lsblk_info, umount
|
from archinstall.lib.disk.utils import get_lsblk_info, umount
|
||||||
from archinstall.lib.exceptions import DiskError, SysCallError
|
from archinstall.lib.exceptions import DiskError, SysCallError
|
||||||
from archinstall.lib.log import debug, info
|
from archinstall.lib.log import debug, info
|
||||||
from archinstall.lib.models.device import CIPHER_KEY_SIZES, DEFAULT_CIPHER, DEFAULT_ITER_TIME
|
from archinstall.lib.models.device import DEFAULT_CIPHER, DEFAULT_ITER_TIME, EncryptionCipher
|
||||||
from archinstall.lib.models.users import Password
|
from archinstall.lib.models.users import Password
|
||||||
from archinstall.lib.utils.util import generate_password
|
from archinstall.lib.utils.util import generate_password
|
||||||
|
|
||||||
|
|
@ -69,21 +69,15 @@ class Luks2:
|
||||||
|
|
||||||
def encrypt(
|
def encrypt(
|
||||||
self,
|
self,
|
||||||
key_size: int = 512,
|
|
||||||
hash_type: str = 'sha512',
|
hash_type: str = 'sha512',
|
||||||
iter_time: int = DEFAULT_ITER_TIME,
|
iter_time: int = DEFAULT_ITER_TIME,
|
||||||
key_file: Path | None = None,
|
key_file: Path | None = None,
|
||||||
cipher: str = DEFAULT_CIPHER,
|
cipher: EncryptionCipher = DEFAULT_CIPHER,
|
||||||
) -> Path | None:
|
) -> Path | None:
|
||||||
debug(f'Luks2 encrypting: {self.luks_dev_path}')
|
debug(f'Luks2 encrypting: {self.luks_dev_path}')
|
||||||
|
|
||||||
key_file_arg, passphrase = self._get_passphrase_args(key_file)
|
key_file_arg, passphrase = self._get_passphrase_args(key_file)
|
||||||
|
|
||||||
# Resolve the correct key size for this cipher.
|
|
||||||
# XTS mode uses two keys (e.g. 256+256 = 512 bits for aes-xts-plain64),
|
|
||||||
# CBC mode uses a single key (max 256 bits for aes-cbc-essiv:sha256).
|
|
||||||
resolved_key_size = CIPHER_KEY_SIZES.get(cipher, key_size)
|
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
'cryptsetup',
|
'cryptsetup',
|
||||||
'--batch-mode',
|
'--batch-mode',
|
||||||
|
|
@ -93,11 +87,11 @@ class Luks2:
|
||||||
'--pbkdf',
|
'--pbkdf',
|
||||||
'argon2id',
|
'argon2id',
|
||||||
'--cipher',
|
'--cipher',
|
||||||
cipher,
|
cipher.value,
|
||||||
'--hash',
|
'--hash',
|
||||||
hash_type,
|
hash_type,
|
||||||
'--key-size',
|
'--key-size',
|
||||||
str(resolved_key_size),
|
str(cipher.key_size),
|
||||||
]
|
]
|
||||||
|
|
||||||
cmd += [
|
cmd += [
|
||||||
|
|
@ -320,4 +314,4 @@ def unlock_luks2_dev(
|
||||||
if not luks_handler.is_unlocked():
|
if not luks_handler.is_unlocked():
|
||||||
luks_handler.unlock()
|
luks_handler.unlock()
|
||||||
|
|
||||||
return luks_handler
|
return luks_handler
|
||||||
|
|
@ -1462,16 +1462,37 @@ class EncryptionType(StrEnum):
|
||||||
return type_to_text[self]
|
return type_to_text[self]
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_CIPHER = 'aes-xts-plain64'
|
class EncryptionCipher(Enum):
|
||||||
|
# None passed to cryptsetup means its built-in default (aes-xts-plain64).
|
||||||
|
# Adiantum is for CPUs without AES acceleration. It is a composite mode:
|
||||||
|
# spec must name both the stream cipher and block cipher
|
||||||
|
# (xchacha12,aes) or the kernel rejects it as unsupported.
|
||||||
|
AES_XTS_PLAIN64 = 'aes-xts-plain64'
|
||||||
|
# xchacha12 = faster (Android default), xchacha20 = wider margin.
|
||||||
|
ADIANTUM_XCHACHA12_PLAIN64 = 'xchacha12,aes-adiantum-plain64'
|
||||||
|
ADIANTUM_XCHACHA20_PLAIN64 = 'xchacha20,aes-adiantum-plain64'
|
||||||
|
# AES finalist, conservative margin (32 rounds), bitslices well
|
||||||
|
# on AVX2 despite no dedicated hw acceleration.
|
||||||
|
SERPENT_XTS_PLAIN64 = 'serpent-xts-plain64'
|
||||||
|
# Wide-block AES mode (AES-NI accelerated), single 256-bit key.
|
||||||
|
AES_HCTR2_PLAIN64 = 'aes-hctr2-plain64'
|
||||||
|
# Non-NIST standard (ISO/NESSIE/CRYPTREC), AVX2 accelerated.
|
||||||
|
CAMELLIA_XTS_PLAIN64 = 'camellia-xts-plain64'
|
||||||
|
# Legacy CBC mode — weaker than XTS against watermarking attacks,
|
||||||
|
# slower due to per-sector ESSIV/SHA256. Included for compatibility.
|
||||||
|
AES_CBC_ESSIV_SHA256 = 'aes-cbc-essiv:sha256'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_size(self) -> int:
|
||||||
|
# XTS uses two keys, so 512 bits => 256-bit cipher. Adiantum
|
||||||
|
# and HCTR2 use a single 256-bit key; 512 makes cryptsetup fail.
|
||||||
|
if '-xts-' in self.value:
|
||||||
|
return 512
|
||||||
|
return 256
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_CIPHER = EncryptionCipher.AES_XTS_PLAIN64
|
||||||
|
|
||||||
# XTS mode splits the key in two halves so needs double the bits.
|
|
||||||
# CBC/ECB use the key directly — max 256 bit.
|
|
||||||
CIPHER_KEY_SIZES: dict[str, int] = {
|
|
||||||
'aes-xts-plain64': 512, # XTS: 256+256
|
|
||||||
'aes-cbc-essiv:sha256': 256, # CBC: single 256-bit key
|
|
||||||
'serpent-xts-plain64': 512, # XTS: 256+256
|
|
||||||
'twofish-xts-plain64': 512, # XTS: 256+256
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DiskEncryptionSerialization(TypedDict):
|
class _DiskEncryptionSerialization(TypedDict):
|
||||||
encryption_type: str
|
encryption_type: str
|
||||||
|
|
@ -1490,7 +1511,7 @@ class DiskEncryption:
|
||||||
lvm_volumes: list[LvmVolume] = field(default_factory=list)
|
lvm_volumes: list[LvmVolume] = field(default_factory=list)
|
||||||
hsm_device: Fido2Device | None = None
|
hsm_device: Fido2Device | None = None
|
||||||
iter_time: int = DEFAULT_ITER_TIME
|
iter_time: int = DEFAULT_ITER_TIME
|
||||||
cipher: str = DEFAULT_CIPHER
|
cipher: EncryptionCipher = DEFAULT_CIPHER
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
if self.encryption_type in [EncryptionType.LUKS, EncryptionType.LVM_ON_LUKS] and not self.partitions:
|
if self.encryption_type in [EncryptionType.LUKS, EncryptionType.LVM_ON_LUKS] and not self.partitions:
|
||||||
|
|
@ -1519,7 +1540,7 @@ class DiskEncryption:
|
||||||
obj['iter_time'] = self.iter_time
|
obj['iter_time'] = self.iter_time
|
||||||
|
|
||||||
if self.cipher != DEFAULT_CIPHER: # Only include if not default
|
if self.cipher != DEFAULT_CIPHER: # Only include if not default
|
||||||
obj['cipher'] = self.cipher
|
obj['cipher'] = self.cipher.value
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
@ -1565,7 +1586,8 @@ class DiskEncryption:
|
||||||
if vol.obj_id in disk_encryption.get('lvm_volumes', []):
|
if vol.obj_id in disk_encryption.get('lvm_volumes', []):
|
||||||
volumes.append(vol)
|
volumes.append(vol)
|
||||||
|
|
||||||
cipher = disk_encryption.get('cipher', None) or DEFAULT_CIPHER
|
cipher_str = disk_encryption.get('cipher', None)
|
||||||
|
cipher = EncryptionCipher(cipher_str) if cipher_str else DEFAULT_CIPHER
|
||||||
|
|
||||||
enc = cls(
|
enc = cls(
|
||||||
EncryptionType(disk_encryption['encryption_type']),
|
EncryptionType(disk_encryption['encryption_type']),
|
||||||
|
|
@ -1664,4 +1686,4 @@ class LsblkInfo(BaseModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fields(cls) -> list[str]:
|
def fields(cls) -> list[str]:
|
||||||
return [field.alias or name for name, field in cls.model_fields.items() if name != 'children']
|
return [field.alias or name for name, field in cls.model_fields.items() if name != 'children']
|
||||||
Loading…
Reference in New Issue