Wednesday, August 8, 2012

Re-encryption of LUKS device (cryptsetup-reencrypt tool)

Do you need to change encryption parameters of LUKS device (like volume key, cipher or IV)? Or even switch not encrypted device into encrypted one with minimal effort?

That's where new cryptsetup-reencrypt tool from cryptsetup 1.5 can help.

What it is about...

With LUKS device, you can easily specify encryption parameters and these are stored to on-disk header.

LUKS allows you to easily set up multiple passphrases to unlock device and easily change these unlocking passphrases later.

But sometimes, even if it should be rare operation, you need to change other attribute like volume encryption key itself or used cipher.

With new cryptsetup-reencrypt you can run required re-encryption in-place and almost automatically.

Cryptsetup-reencrypt is available in Fedora (in Fedora 17 updates and Rawhide/F18), for other distro you need to get cryptsetup 1.5.x sources and recompile with option --enable-cryptsetup-reencrypt.

Important note: it is experimental tool and it doesn't use backup copies while reencryption is running so in the case of hw or power failure you will lose data.

It can restart after intentional suspend though (but you have to preserve temporary files with header).

What cryptsetup-reencrypt tool allows

  • change arbitrary parameter of LUKS encryption
    • volume (master) key
    • key size
    • cipher, cipher mode, IV
    • LUKS header hash algorithm
  • add new LUKS header (switch device from unencrypted to encrypted)
  • shift ciphertext data (for new header or to fix alignment)
Device after reencryption is fully compatible with all versions of cryptsetup/LUKS
  • volume (master) key is always regenerated during re-encryption.
  • some operations (extending key size, adding new encryption layer) requires shift of data (thus some unused space is required in the end of device)
  • the tool requires inactive LUKS device (offline reencryption)
  • devices with detached LUKS headers are not yet supported

Why offline?

The former idea was to run reencryption in-kernel allowing active device use.

While this feature still can be implemented in future, utilizing existing libcryptsetup functions provides solution for most of the use cases.

Also it doesn't require any kernel update and no changes in LUKS on-disk format (the whole solution is quite easily usable in old Linux distro releases - basically you just need to compile cryptsetup 1.5.0).

How it is works?

LUKS device looks like this:

| LUKS header |(padding)| data (ciphertext) ... |

Cryptsetup-reencrypt tool simply creates a new temporary LUKS header and continuously read (with old header parameters) and writes (with new header parameters) data area, using simple metadata header to log progress.

The headers are stored in files and original header on-disk is during encryption marked unusable (known signature is overwritten).

So during re-encryption you have temporarily these files:
  • LUKS-<UUID>.log - re-encryption log
  • LUKS-<UUID>.org - old temporary LUKS header
  • LUKS-<UUID>.new - new temporary LUKS header
No other sensitive data are stored to temporary files.
If the re-encryption is suspended (e.g. by ctrl+c) it will automatically restart if these files are present in current directory.

You will need to enter all keyslot passphrases (or remove some keyslots in advance).

After re-encryption new header is restored to original device and temporary files are deleted.

Example: Change of cipher (and volume key)

Let's change cipher and key on devie /dev/sde1...

# cryptsetup-reencrypt -c twofish-xts-plain64 /dev/sde1
  Enter LUKS passphrase for key slot 0:
  Enter LUKS passphrase for key slot 1:
  Progress: 100.0%, ETA 00:00, 4094 MiB written, speed  25.6 MiB/s

If re-encryption is slow, there are several options like block size and directio which can help - this depends on hw. e.g. here we use 32MiB block and direct-io.
See man cryptsetup-reencrypt for description.

# cryptsetup-reencrypt -B 32 --use-directio /dev/sde1

Example: Switch unencrypted installation to encrypted

To switch device to LUKS from plain filesystem or LVM PV you need to perform these steps:
  • reduce device (and fs) to create space for LUKS header and keyslots (alternatively extend underlying device)
  • create new luks header and re-encrypt data
  • fix system config (crypttab, fstab, etc) to activate modified device stack
  • generate new initramfs to include new tools (for root device)
  • fix kernel boot parameters (for root device)
Following example shows how to switch Fedora 17 minimal install (on LVM) to encrypted system in-place.

Note: This example is intentionally complex.
In fact, nobody should use it (re-installation is much simpler).
But it is nice demonstration how storage stack operations looks like...

I. Preparation

  • start with F17 minimal installation, use ext4, use LVM, use updates repo
    I will use "f17t" as system name -> vg_f17t LVM VG.
  • boot and verify that system is fully updated
  • install needed tools

    # yum install cryptsetup cryptsetup-reencrypt
  • regenerate initramfs to include tools we will use in ramdisk
    Do NOT forget quotes below or you will overwrite lsblk by ramdisk image!

    # dracut -f -I "/sbin/cryptsetup /sbin/cryptsetup-reencrypt /sbin/resize2fs /bin/lsblk"

II. boot into dracut shell and manually do all what's needed

  • in grub menu, edit parameters and add rdbreak=pre-mount
  • boot - you should get dracut shell environment
  • you should see something like this storage space

    # lsblk -o name,fstype

      ├─sda1                       ext4
      └─sda2                       LVM2_member
        ├─vg_f17t-lv_swap (dm-0)   swap
        └─vg_f17t-lv_root (dm-1)   ext4

Boot partition (sda1) remains, sda2 (lvm PV) will be shifted and LUKS header added (and encrypted).
Free 4M is enough for keyslots and header. Unfortunately, it is LVM so we need to check extent size. (You can allocate space only in multiple of extents.)
  • now create some space for LUKS header
    The whole goal of this exercise is to free last 4M space of /dev/sda2.
  • Be sure we have lvm volumes activated

    # lvm vgchange -a y
  • we need to free last extents on device, need to figure out which LV need to be resized (swap or root?)

    # lvm vgs -o name,extent_size

    VG      Ext
    vg_f17t 32.00m

    So extent is 32MiB, so we have to reduce last LV of 1 extent/32M.
  • which LV is allocated there?

    # lvm lvs -o lv_name,seg_pe_ranges

    LV        PE Ranges
    lv_root   /dev/sda2:63-239
    lv_swap   /dev/sda2:0-62

    The number is start-end extent, so last extent is allocated by lv_root.
    Nice, another complication with filesystem.
  • Well, let's simplify it: resize fs to minimum.
    (I expect it will be always enough, if not, your system is doomed anyway ;-) You will get used blocks in e2fsck output.

    # e2fsck -f /dev/vg_f17t/lv_root

    # resize2fs -M /dev/vg_f17t/lv_root

    The filesystem on /dev/vg_f17t/lv_root is now <n> blocks long.
  •  resize LV - reduce one extent, note "-1" argument and lower case "l" for extents.
    LVM uses locking which is not available in dracut by default. I will use trick to overwrite locking type on commandline. Alternatively you can edit /etc/lvm/lvm.conf directly.

    # lvm lvresize -l-1 vg_f17t/lv_root --config 'global {locking_type=1}'

    Logical volume lv_root successfully resized.
  • You can now verify it with lvs -o+seg_pe_ranges
  • resize fs back to full device (no size specified == size of LV)

    # resize2fs /dev/vg_f17t/lv_root
  • Let's ignore pvresize now (we will do it later - out of device space is unused).
  • deactivate whole VG

    # lvm vgchange -a n
  • reencrypt with data shift and adding new header

    # cryptsetup-reencrypt -N --reduce-device-size 4MiB /dev/sda2

    Enter new LUKS passphrase:
    Progress: 100.0%, ETA 00:00, 7687 MiB written, speed 33.9 MiB/s

III. Continue to boot system manually, now with LUKS

  • activate LUKS device.
    Note I will use more friendly name "sda2_crypt" instead of Fedora ugly default "luks-UUID" name.

    # cryptsetup luksOpen /dev/sda2 sda2_crypt
  • activate LVs

    # lvm vgchange -a y
  • continue boot - just use ctrl+d and dracut should bring system up
    Do not reboot yet.

IV. Fix system configuration

  • Now you should see LUKS in storage stack

    # lsblk -o name,fstype

    ├─sda1                       ext4
    └─sda2                       crypto_LUKS
      └─sda2_crypt (dm-0)        LVM2_member
        ├─vg_f17t-lv_swap (dm-1) swap
        └─vg_f17t-lv_root (dm-2) ext4
  • fix PV device size in lvm metadata

    # pvresize /dev/mapper/sda2_crypt
  • get UUID of new LUKS

    # blkid /dev/sda2
  • edit, or create new /etc/cryptab and add line

    sda2_crypt UUID=<LUKS UUID above> none
  • edit grub to not ignore LUKS devices
    remove rd.luks=0 in /etc/default/grub
    Precisely we should add UUID of device which contains root fs, but default is activate all.
  • regenerate grub.conf

    # grub2-mkconfig -o /boot/grub2/grub.cfg
  • regenerate dracut initramfs

    # dracut -f

V. Reboot & profit.

  • You just get advanced LVM training for free :-)

Example: Regenerate key

Now more realistic use cases
  • you have old encrypted laptop and cannot reinstall it for now but you are not sure if old user had some LUKS header metadata backups which allows unlocking device even if passphrase was changed
  • you have an image for standard install of your company notebooks and want to simplify installation by dd of this image and personalize it on first boot (regenerate key, UUIDs, passwords).
    It is pretty stupid to have the same volume key on all laptops, isn't it? :-)
  • for some reason (security policy, etc) you just want to regenerate volume key

Simply run cryptsetup-reencrypt on the LUKS device (for system device use live-CD) as in the first example.

There is also proof-of-concept dracut module for reencryption in initramfs (but it is very tricky and it is doing a lot of presumptions about the system like stable kernel name - laptop with one disk - etc. But maybe it can be useful for someone.)

Please be sure your kernel RNG is properly seeded before automatic reencryption. (Also see --use-random/--use-urandom options.)

Warning & Co.

Note that LUKS re-encrypt tool is experimental and it is not resistant to media errors and power failure. Also if you stop re-encryption and remove temporary files, you lost the whole device.

Never ever use it without reliable backup.

Friday, October 7, 2011

Šifrování disků (nejen) v Linuxu -

Slajdy z mého povídání o šifrování na 39. konferenci jsou na

Přednáška popisovala základní principy používané pro šifrování disků (Full Disk Encryption) a popis vlastností některých používaných systémů (Truecrypt, loop-AES, BitLocker (Windows) a dm-crypt/LUKS.

A pokud by někoho spíše zajímala historie, návštěva kláštera v Želivu, v jehož prostorách se tentokrát konference konala, a připomenutí jeho více než smutné poválečné historie za to opravdu stojí.

Sunday, August 14, 2011

TRIM & dm-crypt ... problems?

Some SSDs (Solid State Drive) implement TRIM command which is used to inform the disk (block device) that sectors are no longer used. If you are using software FDE (Full Disk Encryption) with such device, TRIM could have some side and unexpected effects. Linux kernel 3.1 (with user space support in cryptsetup 1.4.x) will optionally support TRIM on dm-crypt encrypted disks.

This short article shows illustrative example what you can expect after you enable TRIM support with your encrypted disk.

Disk & Sectors

Disk (SSD, flash or similar device) is block device which uses blocks as atomic units. Device allows to read or write only multiple of blocks. All reads or writes of blocks are independent.

Because blocks can have different meaning in the encryption layer (as cipher blocks), I will always use "sector" in the following text (hardware sector can have different size but block encryption, as implemented in current kernel, always works with 512 bytes sector).


TRIM is operation where upper layer (e.g. filesystem) informs the device which sectors are no longer used (do not contain valid data anymore) and that device do not need to keep data content.

In Linux terminology is this operation called discard. In SCSI world you can see UNMAP command. In principle, all these names describe the same operation.

Discard operation can be used for two purposes:
  • for Thin Provisioning (informs that data area can be returned to allocation pool)
  • for SSD operation optimization
We are mainly interested in the second case.

Internal architecture of SSDs uses larger blocks than sectors. These blocks must be erased before write can be performed and information about sectors which do not need to keep data is very useful for the drive firmware. Drive than can very effectively organize blocks such way that data fragmentation is avoided and the disk lifetime is maximized (also see wear-leveling).

Fig. 1: TRIM block rewrite

This is simple example what happens when SSD need to rewrite a sector with and without TRIM.

On the other side, TRIM is usually overrated. Drive itself should keep good performance even without TRIM, either by using internal garbage collecting process or by some area reserved for optimal writes handling.

Forensic analysis

Very important is that TRIM decreases ability to recover data after an admin error.

TRIM practically eliminates using of standard recovery and forensic analysis tools for data recovery of "erased" areas of disk.

Imagine a situation that you wrongly recreate partition table. This mistake is easy to fix but not when the data area is completely erased by previous TRIM command.

What really happens with the sectors after TRIM depends on disk firmware. It could be still possible to recover some data from the NAND chips.

Most of the SSDs do not define what read operation returns if sector was discarded. Usually it returns empty data (sectors full of zeroes) but without explicit support kernel cannot rely on it.


Next figure shows 8GB disk partition with some pseudo graphical representation (every 512 bytes sector was represented by one pixel in original image).

Disk was wiped with specific pattern and test program later tags sectors according to detected data (zeroed sectors, non-random, random-like data). 
Detection of random data is only approximate, in fact the test uses check for number of bits set and trivial Chi-square test with nonstandard boundaries.

This partition contains copy of /usr and also it contains 512MB image of Truecrypt device (in file).

Fig. 2: Used fs image
Now the same but after TRIM operation executed with fstrim(8).
Tested SSD always returns zeroes after TRIM here.

Fig.3: Used fs image with TRIM

And yet again, now with all files removed from filesystem.
Fig. 4: Erased fs image with TRIM

It is clearly visible that TRIM did its job. Discarded areas depend on filesystem type (other filesystems on test system did not supported TRIM unfortunately).

Disk encryption

Disk encryption on the sector level is fully transparent. For example, filesystem can work with encrypted disk without any changes required. Encryption layer is simple inserted between underlying device and upper layers.

In other words, disk contains encrypted sectors which are decrypted and upper layer see virtual block device where sectors are already decrypted. The disk containing encrypted sectors is called ciphertext device, virtual disk with decrypted sectors plaintext device.

For explaining TRIM it is not important which algorithm and encryption mode is used (for more info see FDE description).

Important is that ciphertext data (encrypted sectors) cannot be distinguished from random data without key knowledge. If the ciphertext device is wiped off using random data (or sectors are at least once written though encryption layer) it is not possible to say which sectors contain data and which are unused.

Ciphertext device without using TRIM from Figure 2 would be just black box (all sectors have random data characteristic, disk was wiped before use).

Ciphertext of Figure 3 is more interesting. Here is the first problem - unused sectors are clearly visible (discarded sectors contains detectable pattern - zeroes).

Fig.5: Used ciphertext device with TRIM

And now the same with erased files. It is apparent that even without key we can say that disk contains almost no data and according to some metadata patterns we can even guess used filesystem.

Fig.6: Erased ciphertext device with TRIM
And now see how sector test looks like for ciphertext device and virtual plaintext device (note plaintext device is virtual device over ciphertext device - e.g. LUKS mapping).
Fig.7: ciphertext and plaintext device with TRIM

The TRIM command is forwarded directly to the ciphertext device. But reads from discarded areas are processed as the same as normal reads (thus decrypted) and for the plaintext device these are not empty areas but areas full of "random noise" data (in fact decrypted sectors with zeroes as ciphertext).

The upper layer must not rely on zeroed areas after TRIM even if underlying device announces such function. For dm-crypt it is guaranteed by setting "discard_zeroes_data" to zero always.

But if some forensic analysis tool tries to find something here... Good luck.

  • If there is a strong requirement that information about unused sectors must not be available to attacker, TRIM must be always disabled.
  • TRIM must not be used if there is a hidden device on the disk.
    (In this case TRIM would either erase the hidden data or reveal its position.)
  • If TRIM is enabled and executed later (even only once by setting option and calling fstrim), this operation is irreversible.
    Discarded sectors are still detectable even if TRIM is disabled again.
  • In specific cases (depends on data patterns) some information could leak from the ciphertext device.
    (In example above you can recognize filesystem type for example.)
  • Encrypted disk cannot support functions which rely on returning zeroes of discarded sectors (even if underlying device announces such capability).
  • Recovery of erased data on SSDs (especially using TRIM) requires completely new ways and tools.
    Using standard recovery tools is usually not successful. 

If you decided to turn on TRIM for your device... You have been warned ;-)