new ciphers

This commit is contained in:
Alperen42v 2026-07-03 14:12:59 +03:00
parent f8a202d843
commit ed7ba45ae3
4 changed files with 54 additions and 42 deletions

View File

@ -23,6 +23,7 @@ from archinstall.lib.models.device import (
BtrfsMountOption,
DeviceModification,
DiskEncryption,
EncryptionCipher,
FilesystemType,
LsblkInfo,
ModificationStatus,
@ -281,7 +282,7 @@ class DeviceHandler:
enc_password: Password | None,
lock_after_create: bool = True,
iter_time: int = DEFAULT_ITER_TIME,
cipher: str = DEFAULT_CIPHER,
cipher: EncryptionCipher = DEFAULT_CIPHER,
) -> Luks2:
luks_handler = Luks2(
dev_path,
@ -627,4 +628,4 @@ class DeviceHandler:
self._wipe(block_device.device_info.path)
device_handler = DeviceHandler()
device_handler = DeviceHandler()

View File

@ -11,6 +11,7 @@ from archinstall.lib.models.device import (
DEFAULT_ITER_TIME,
DeviceModification,
DiskEncryption,
EncryptionCipher,
EncryptionType,
Fido2Device,
LvmConfiguration,
@ -112,7 +113,7 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
return await select_lvm_vols_to_encrypt(self._lvm_config, preset=preset)
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)
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_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
enc_partitions = self._item_group.find_by_key('partitions').value
enc_lvm_vols = self._item_group.find_by_key('lvm_volumes').value
@ -214,9 +215,9 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
return 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:
return f'{tr("Encryption cipher")}: {cipher}'
return f'{tr("Encryption cipher")}: {cipher.value}'
return 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
async def select_encryption_cipher(preset: str | None = None) -> str | None:
# Regular block-cipher modes: accepted directly by `cryptsetup --cipher`.
options = [
'aes-xts-plain64',
'aes-cbc-essiv:sha256',
'serpent-xts-plain64',
'twofish-xts-plain64',
]
async def select_encryption_cipher(preset: EncryptionCipher | None = None) -> EncryptionCipher | None:
options = list(EncryptionCipher)
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.set_focus_by_value(preset)
result = await Selection[str](
result = await Selection[EncryptionCipher](
group,
header=tr('Select encryption cipher'),
allow_skip=True,
@ -458,4 +453,4 @@ async def select_encryption_cipher(preset: str | None = None) -> str | None:
case ResultType.Skip:
return preset
case ResultType.Selection:
return result.get_value()
return result.get_value()

View File

@ -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.exceptions import DiskError, SysCallError
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.utils.util import generate_password
@ -69,21 +69,15 @@ class Luks2:
def encrypt(
self,
key_size: int = 512,
hash_type: str = 'sha512',
iter_time: int = DEFAULT_ITER_TIME,
key_file: Path | None = None,
cipher: str = DEFAULT_CIPHER,
cipher: EncryptionCipher = DEFAULT_CIPHER,
) -> Path | None:
debug(f'Luks2 encrypting: {self.luks_dev_path}')
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 = [
'cryptsetup',
'--batch-mode',
@ -93,11 +87,11 @@ class Luks2:
'--pbkdf',
'argon2id',
'--cipher',
cipher,
cipher.value,
'--hash',
hash_type,
'--key-size',
str(resolved_key_size),
str(cipher.key_size),
]
cmd += [
@ -320,4 +314,4 @@ def unlock_luks2_dev(
if not luks_handler.is_unlocked():
luks_handler.unlock()
return luks_handler
return luks_handler

View File

@ -1462,16 +1462,37 @@ class EncryptionType(StrEnum):
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):
encryption_type: str
@ -1490,7 +1511,7 @@ class DiskEncryption:
lvm_volumes: list[LvmVolume] = field(default_factory=list)
hsm_device: Fido2Device | None = None
iter_time: int = DEFAULT_ITER_TIME
cipher: str = DEFAULT_CIPHER
cipher: EncryptionCipher = DEFAULT_CIPHER
def __post_init__(self) -> None:
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
if self.cipher != DEFAULT_CIPHER: # Only include if not default
obj['cipher'] = self.cipher
obj['cipher'] = self.cipher.value
return obj
@ -1565,7 +1586,8 @@ class DiskEncryption:
if vol.obj_id in disk_encryption.get('lvm_volumes', []):
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(
EncryptionType(disk_encryption['encryption_type']),
@ -1664,4 +1686,4 @@ class LsblkInfo(BaseModel):
@classmethod
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']