Goals
Goal is to provide a simple GUI tool that allows an user to change volume encryption passphrases, and to recover from a lost passphrase using a backup passphrase (created using volume_key --create-backup-passphrase
or the --backuppassphrase
kickstard directive).
High-level discussion
Changing the user's own passphrase is simple - add the new passphrase into an empty slot, and delete the old passphrase. (Changing the passphrase by overwriting the original slot is possible, but dangerous - if the operation were interrupted, neither the old nor the new passphrase would work. Changing the passphrase in place would be the only option if all slots were full, this is considered unlikely.)
To use a backup passphrase, we must first be able to identify that the provided passphrase is the backup passphrase. This can be done by reserving a specific slot for the backup passphrase, but this has the same disadvantage as changing the user's passphrase in place - the backup passphrase might become corrupted while changing it. Another option is to take advantage of the unencrypted metadata in the default volume_key format when using certificates (--output-format asymmetric_wrap_secret_only
), which includes slot number of the created backup passphrase. Thus, after creating the backup passphrase, the backup passphrase slot number would be stored locally, and we detect the backup passphrase by checking the slot number.
Using a backup passphrase would delete all other passphrases (to make sure we never run out of key slots), and set up a new user passphrase. Because the backup passphrase was probably communicated by telephone, and written down, it should be discarded and replaced by a new passphrase. There are three possible times for generating a new backup passphrase.
- As soon as the backup passphrase is used, delete it and generate a new backup passphrase. Queue the newly created backup passphrase escrow packet for sending to the central escrow server. This has the disadvantage that if the user forgets their password again before connecting to the escrow server, the IT department can not provide a backup passphrase to the user.
- When the backup passphrase is used, keep it, but create a new backup passphrase in another slot. Queue the newly created backup passphrase escrow packet for sending to the central escrow server. When the central escrow server receives the packet, it queues a task of removing the old backup passphrase on the machine. This fixes the disadvantage of the previous approach, but requires larger implementation effort to automate the old backup passphrase removal.
- When the backup passphrase is used, keep it and do not create a new backup passphrase. Only when the IT department can remotely manage the machine, create a new backup passphrase, escrow it, and delete the old backup passphrase. (This process can be either automated or completely manual.) This has the disadvantage that either the escrow recovery mechanism needs to be used twice by the IT department, (when providing the backup passphrase to the user, and when creating the new backup passphrase), or the IT department needs to store the unencrypted backup passphrase as part of the queued request to replace the backup passphrase. (This was the originally approach considered during volume_key design, with slots implicitly identified using the backup passphrase.)
The second option has the least disadvantages, and can be automated reasonably easily.
Additional concern is identifying which encrypted volume to act upon. In the simplest case, there is only one encrypted volume in the machine (the main hard drive). In other cases, the user needs to be able to select a volume. In theory we could follow the device-mapper/RAID/partition mappings to e.g. find the encrypted volume used for the "/" partition, in practice letting the user choose from all known encrypted volumes (e.g. as listed in /etc/crypttab
would probably be good enough.
Implementation notes
This section lists in details the necessary steps for the underlying implementation mechanism (ignoring the GUI), and the relevant implementation interfaces (using the pycryptsetup
Python module; in addition, libcryptsetup
provides a C API, which is more complete and easier to use than the Python API)
Necessary Python routines are pasted at Encrypted volume passphrase change infrastructure.
In permanent, root-only storage (e.g. somewhere in /etc
), store for each encrypted volume (identified by LUKS UUID), the following:
- number of the slot containing the currently escrowed backup passphrase, if any ("
current_backup_slot
") - number of the slot containing a new backup passphrase, pending escrow, if any ("
pending_backup_slot
")
Post-installation operations
For each backup passphrase escrow packet created by anaconda and stored in /root
:
- Read the escrow packet metadata, extract the volume UUID and passphrase slot.
Python implementation is not yet ready, will be provided later. - Store the passphrase slot as
current_backup_slot
in permanent storage.
Operations when the user requests a passphrase change
- List all known encrypted volumes. If there is more than one encrypted volume, let the user select one.
Read/etc/crypttab
and the output ofdmsetup table
. - Given a volume, extract its UUID, and read
current_backup_slot
andpending_backup_slot
from permanent storage.
Usepycryptsetup.CryptSetup.luksUUID
. - Prompt the user for a passphrase; verify it and get the slot number for this passphrase ("
input_slot
"). If the passphrase is invalid, ask again or exit.
Seepassphrase_slot()
in the attached code. -
If the
input_slot == pending_backup_slot
, abort - the user should not have known the pending backup passphrase, and should not have been able to guess it. -
If
input_slot != current_backup_slot
, the user is changing their own passphrase:- Ask for a new passphrase, add it to a free slot.
Usepycryptsetup.CryptSetup.addKey
. - Delete the old passphrase.
Usepycryptsetup.CryptSetup.removeKey
.
- Ask for a new passphrase, add it to a free slot.
-
Otherwise, the user is using the backup passphrase:
-
Clear all slots except for
current_backup_slot
.
Usecryptsetup luksKillSlot
or the C API. - Unset
pending_backup_slot
and delete the queued escrow packet for this volume, if any. - Ask for a new passphrase, add it to a free slot.
Usepycryptsetup.CryptSetup.addKey
. - Generate a new backup passphrase, add it, and create the corresponding escrow packet.
Seeadd_backup_passphrase()
in the attached code. - Read metadata of the newly created escrow packet, set
pending_backup_slot
accordingly.
Python implementation is not yet ready, will be provided later. - Queue the newly created packet for sending to the escrow server.
-
Clear all slots except for
After the queued escrow packet is stored on the escrow server
(Is this initiated by the client or by the server?)
- Verify that the client does not have an escrow packet queued that is newer than the packet stored on the server.
- Delete the queued escrow packet (if this was not already done when storing it on the server).
- Read the value of <current_backup_slot> ("
old_slot
") - Set
current_backup_slot
topending_backup_slot
. - Unset
pending_backup_slot
. - Clear
old_slot
Usecryptsetup luksKillSlot
or the C API.
Implementation questions
- Implementation language: Python, C?
- Privilege escalation mechanism: D-Bus/policykit, userhelper?
Long-term infrastructure improvements
These are not strictly necessary for the first version, but they would allow a more efficient implementation (starting fewer subprocesses) and probably better error handling.
- Replace the Python escrow packet metadata parser by a new API in
libvolume_key
- Update python-cryptsetup to expose all of the new libcryptsetup APIs, and not start separate
cryptsetup
child processes.