Introduction
This HOWTO shows you how to take a completely new hard disk and encrypt its entire contents using dm-crypt. Actually, it's the partitions that are encrypted, not the disk itself (details below). So if you wish, you can make multiple partitions with different passphrases, or some not encrypted. With a bit of work, you can adapt these instructions to make an encrypted partition on an existing disk, possibly with some unencrypted partitions on the same disk.
dm-crypt asks for a passphrase before you can mount the disk. This provides good protection against your PC getting stolen - once they reboot your PC, the thieves have lost access to your data. Throughout, I will use the 'cryptsetup' interface to dm-crypt.
Note : if you want your encryption to defeat a full cryptoanalytic attack, not just casual snooping, you need to fill the disk with
high quality random data. Badblocks below justs uses 'libc' random() to generate a fixed-length sequence and fills the disk with repeats of this sequence, but is fast (your limitation will be disk speed, not CPU speed). /dev/urandom is a lot better (takes about 5 minutes per gigabyte on my system), /dev/random is best (takes about 1 year per gigabyte on my system, much too slow!).
Note : /dev/urandom is very taxing on the CPU, as it generates only 160 bits for every SHA-1 computation. If you find it too slow, then you may use /dev/zero to the *encrypted* dm target after creating the crypto mapping. This is a more efficient use of cryptographic primitives, as now you're using dm-crypt as it was designed, instead of using SHA-1 as a PRNG. I have verified that these zero-filled sectors will not be identical, which suggests that this is suitably secure. If it is not secure by some chance, then dm-crypt itself has a serious weakness, as filesystems tend to have repeated blocks of data on them from time to time. At worst, your opponent will be able to tell how much of the filesystem is used, and have a known plaintext (all zeroes) for a cryptanalytic attack. This method is a bit less conservative than filling it with PRNG data unrelated to the encryption key. However, on my system it is almost 30x faster.
Step1a : (Optional) Check the hard disk for errors (side effect, fill it with random data).
It's probably a good idea to check your entire disk for errors before you start. Not only is this good practice, but modern hard disks contain a few 'spare' sectors, and if they detect errors in reading, they can silently replace the bad sector with a backup sector (this is invisible to the OS). So writing and reading the entire disk before you start should allow this to happen.
You need to know what device your new disk is attached to. For PATA disks (parallel ATA) on Linux, they'll probably be /dev/hda, /dev/hdb and so on. For SATA, SCSI or USB attached disks, they'll probably be /dev/sda, /dev/sdb, /dev/sdc and so on. I am attaching a USB disk, I already have 2 SATA disks so this disk seems to appear under /dev/sdc.
Make sure you find out, and adapt the below commands as necessary, or you may overwrite your existing data!
It's good to fill an encrypted disk with initial random data. This makes breaking the passphrase so much harder. The below method is sufficient for a casual attack but is not 'random enough' to defeat sophisticated cryptographers. If you need that, use the '/dev/urandom' method below or read a good book on cryptography and random numbers!
I recommend the disk check and the fill with random data be done at the same time. Read the man page for more details on this command:
# /sbin/badblocks -c 10240 -s -w -t random -v /dev/sdc
(wait several hours...)
Checking for bad blocks in read-write mode
From block 0 to 295360984
done
Reading and comparing: done
Pass completed, 0 bad blocks found.
#
This will take some time, on my USB-attached 300Gb disk it took around 8 hours. Phase 1 will write random data to the disk, phase 2 will read it back and verify it.
Step 1b (Optional) Fill the disk with random data
If you didn't do step 1a, do step 1b. This will take a long time (around 5 minutes per gigabyte on my system), because generating good quality random data is very CPU intensive. Method 1a has an easy progress indicator, while "dd" only shows its progress when a USR1 signal is sent to it ("kill -USR1 `pidof dd`"). However, this method is 'more random' (and more secure) than the primitive random number generator included in 'badblocks', above.
# dd if=/dev/urandom of=/dev/sdc
(wait several hours...)
#
Step 2 : Partition the disk
Remember, the data on a hard disk consists of (1) a partition table (2) one or more partitions.
The way dm-crypt works is, you mount an encrypted partition. So we won't encrypt the whole hard disk, rather we'll create a partition table (unencrypted) as usual, then create one or more partitions on the disk, as usual, except the partition(s) can be encrypted if we choose.
So, we partition the hard disk. In my case, I am partitioning my entire 300Gb hard disk as a single partition.
# /sbin/fdisk /dev/sdc
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that, of course, the previous
content won't be recoverable.
The number of cylinders for this disk is set to 36481.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
(e.g., DOS FDISK, OS/2 FDISK)
Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)
Command (m for help): p
Disk /dev/sdc: 300.0 GB, 300069052416 bytes
255 heads, 63 sectors/track, 36481 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Device Boot Start End Blocks Id System
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-36481, default 1): 1
Last cylinder or +size or +sizeM or +sizeK (1-36481, default 36481): (enter)
Using default value 36481
Command (m for help): p
Disk /dev/sdc: 300.0 GB, 300069052416 bytes
255 heads, 63 sectors/track, 36481 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Device Boot Start End Blocks Id System
/dev/sdc1 1 36481 293033601 83 Linux
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
#
Step 3 : Create mapping between logical and physical partitions
dm-crypt works by transparently translating (in the kernel) between a physical on-disk partition (which is encrypted) and a logical partition which you can then mount and use as normal. This step takes no time and writes no data, it just establishes the mapping for future use.
The physical (encrypted) partition will be /dev/something
The logical (unencrypted) partition will be /dev/mapper/something2
I prefer to keep the names the same, so something=something2. That makes things easier to remember.
# /usr/bin/cryptsetup --verbose --verify-passphrase create sdc1 /dev/sdc1
Enter passphrase: (enter your passphrase, and write it down somewhere!)
Verify passphrase: (repeat passphrase)
#
Step 4 : Create a filesystem on the logical partition
This is just like making a normal filesystem, just point 'mkfs' at the logical partition. Use your favourite options, filesystem type etc (I use ext3) or just copy my options. As always, remember to change 'sdc1' as appropriate. Note: you do *not* want journalling, or else writes will have to be encrypted twice (once to journal and once when committing journal to final resting place).
# /sbin/mkfs.ext3 -m 1 -O dir_index,filetype,sparse_super /dev/mapper/sdc1
(wait several minutes...)
mke2fs 1.35 (28-Feb-2004)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
36634624 inodes, 73258400 blocks
732584 blocks (1.00%) reserved for the super user
First data block=0
2236 block groups
32768 blocks per group, 32768 fragments per group
16384 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 39 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
#
Step 5 : Mount the Filesystem
This is just like a normal mount, except you use the logical (/dev/mapper) device. First make a mount point if necessary. In my case I am using /home4
# mkdir /home4
# mount /dev/mapper/sdc1 /home4
# df -H
Filesystem Size Used Avail Use% Mounted on
/dev/hda3 11G 5.2G 4.2G 56% /
/dev/hda1 510M 63M 395M 14% /boot
/dev/hda2 11G 3.5G 5.9G 37% /usr
none 797M 0 797M 0% /dev/shm
/dev/mapper/sdc1 296G 34M 294G 1% /home4
#
Hoorah! Now just use /home4(or whatever) as normal. In my case, I now have another 300Gb to play with

It may be slower than a normal disk, due to the CPU time required to encrypt/decrypt. If you wish, use /sbin/hdparm to benchmark. However my benchmarks on an AMD Athlon 3200 indicate no great difference between an encrypted and a normal unencrypted partition.
Step 6: Mounting and unmounting in future
This is a simple procedure. You may be even able to edit your /etc/rc.local script to prompt for a password and mount your encrypted partition at boot.
To mount:
# cryptsetup create sdc1 /dev/sdc1
# mount /dev/mapper/sdc1 /home4
#
To unmount:
# umount /home4
# cryptsetup remove sdc1
#
(Written by William Owen Smith, 15 July 2005. To contact me see here.)