From Fedora Project Wiki

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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).

Is a command-line interface required?

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.

Certificate encryption required
Metadata is available in plaintext only when certificate encryption is used; passphrase-based encryption currently encrypts the metadata as well.

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.

  1. 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.
  2. 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.
  3. 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 of dmsetup table.
    Multiple volumes with a shared passphrase
    If there is more than one encrypted volume, and all share the same passphrase (the setup created by anaconda by default), the tool could prompt for passphrases once, and perform the operations on all relevant volumes. (Recovery from errors would be somewhat difficult, cryptsetup luksHeaderBackup might be useful.)
  • Given a volume, extract its UUID, and read current_backup_slot and pending_backup_slot from permanent storage.
    Use pycryptsetup.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.
    See passphrase_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.
      Use pycryptsetup.CryptSetup.addKey.
    • Delete the old passphrase.
      Use pycryptsetup.CryptSetup.removeKey.
  • Otherwise, the user is using the backup passphrase:
    • Clear all slots except for current_backup_slot.
      Use cryptsetup 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.
      Use pycryptsetup.CryptSetup.addKey.
    • Generate a new backup passphrase, add it, and create the corresponding escrow packet.
      See add_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.

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 to pending_backup_slot.
  • Unset pending_backup_slot.
  • Clear old_slot
    Use cryptsetup 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.