Hibernation with Encrypted Swap



This is a tutorial on how I set up hibernation to an encrypted swap partition with a persistent gpg-encrypted key on Gentoo.

My setup is a laptop with /home, /tmp, and swap all encrypted using luks with a gpg-encrypted master key, stored on the root partition. Setting this up without hibernation is easy, especially using Gentoo's dmcrypt init script. I hacked the init script so that I only had to type the passphrase once and the key was then stored in a variable in the bash script that was used to decrypt the rest of the partitions. I think this is safe because the root partition is only mounted read-only at this time and there is no swap mounted yet so the only place the cleartext key could be stored is in RAM. The patch is shown below. Apply it to /lib/rcscripts/dm-crypt-startup.sh. My installed version of cryptsetup was 1.1.2.

--- /usr/portage/sys-fs/cryptsetup/files/1.0.6-r2-dm-crypt-start.sh	2009-06-16 15:07:01.000000000 -0600
+++ dm-crypt-start.sh	2010-11-25 22:36:40.000000000 -0700
@@ -1,5 +1,7 @@
 # /lib/rcscripts/addons/dm-crypt-start.sh
 
+local common_key_plaintext
+
 # For backwards compatability with baselayout < 1.13.0
 dm_crypt_execute_checkfs() {
 	dm_crypt_execute_dmcrypt
@@ -140,11 +142,12 @@
 		: ${gpg_options:='-q -d'}
 		# gpg available ?
 		if type -p gpg >/dev/null ; then
+			
 			for (( i = 0 ; i < 3 ; i++ ))
 			do
 				# paranoid, don't store key in a variable, pipe it so it stays very little in ram unprotected.
 				# save stdin stdout stderr "values"
-				gpg ${gpg_options} ${key} 2>/dev/null | cryptsetup ${options} ${arg1} ${arg2} ${arg3}
+				gpg ${gpg_options} ${key} | cryptsetup ${options} ${arg1} ${arg2} ${arg3}
 				ret="$?"
 				[ "$ret" -eq 0 ] && break
 			done
@@ -155,6 +158,12 @@
 			einfo "You have to install app-crypt/gnupg first."
 			einfo "If you have /usr on its own partition, try copying gpg to /bin ."
 		fi
+	elif [ "$use_gpg_common_key" == "true" ]; then
+	
+		echo -n "${common_key_plaintext}" | cryptsetup ${options} --key-file - ${arg1} ${arg2} ${arg3}
+		ret="$?"
+		eend "${ret}" "failure running cryptsetup" 
+	
 	else
 		if [ "$mode" == "reg" ]; then
 			cryptsetup ${options} -d ${key} ${arg1} ${arg2} ${arg3}
@@ -233,8 +242,29 @@
 	esac
 }
 
+decrypt_gpg_common_key() {
+	
+	einfo "Decrypting common key file..."
+
+	# gpg available ?
+	if type -p gpg >/dev/null ; then
+		for (( i = 0 ; i < 3 ; i++ ))
+		do
+			common_key_plaintext=`gpg --quiet --decrypt $gpg_options $gpg_common_key`
+			ret="$?"
+			[ "$ret" -eq 0 ] && break
+		done
+		eend "${ret}" "failure running gpg"
+	else
+		ewarn "The GPG common key will not be decrypted ..."
+		einfo "Reason: cannot find gpg application."
+		einfo "You have to install app-crypt/gnupg first."
+		einfo "If you have /usr on its own partition, try copying gpg to /bin ."
+	fi
+}
+
 local cryptfs_status=0
-local gpg_options key loop_file target targetline options pre_mount post_mount source swap remdev
+local gpg_options key loop_file target targetline options pre_mount post_mount source swap remdev gpg_common_key use_gpg_common_key
 
 CMDLINE="`cat /proc/cmdline`"
 for x in ${CMDLINE}
@@ -261,15 +291,21 @@
 
 		# check for the start of a new target/swap
 		case ${targetline} in
+			
+			gpg_common_key=*)
+				eval "${targetline}"
+				decrypt_gpg_common_key
+				;;
+
 			target=*|swap=*)
 				# If we have a target queued up, then execute it
 				dm_crypt_execute_${SVCNAME}
 
 				# Prepare for the next target/swap by resetting variables
-				unset gpg_options key loop_file target options pre_mount post_mount source swap remdev
+				unset gpg_options key loop_file target options pre_mount post_mount source swap remdev use_gpg_common_key
 				;;
 
-			gpg_options=*|remdev=*|key=*|loop_file=*|options=*|pre_mount=*|post_mount=*|source=*)
+			gpg_options=*|remdev=*|key=*|loop_file=*|options=*|pre_mount=*|post_mount=*|source=*|use_gpg_common_key=*)
 				if [[ -z ${target} && -z ${swap} ]] ; then
 					ewarn "Ignoring setting outside target/swap section: ${targetline}"
 					continue


The patch adds two configuration options to /etc/conf.d/dmcrypt, gpg_common_key and use_common_key. Set gpg_common_key to the path of the common key and for each target that uses the common key set use_common_key to 'true' (lower-case, this is my first bash scripting exercise ever so I was too lazy to find out how to handle other possibilities). When dmcrypt starts up it will first ask for the passphrase for the common key, decrypt it, and then use it to decrypt all of the targets that use the common key without prompting you again.

However, the problem is you still can't hibernate to your encrypted swap (though this does solve the annoying problem of having to enter the passphrase three times on a regular boot-up). Fortunately there is Gentoo Bug 217959 which is a hack created by another helpful soul which decrypts the swap partition before TuxOnIce starts looking, so you can hibernate to your encrypted swap! Because the patch has not yet (as of December 2010) been officially integrated into the Portage tree you'll need to create a local overlay and then apply the ebuild patch from the bug report and copy the patch to the files directory.

Then you can create a ramdisk with genkernel --luks --gpg ramdisk that will decrypt the gpg-encrypted key and use that on the swap partition during boot-up. You need to add three parameters to the kernel boot-line, crypt_swap, the path to the swap partition (or LVM volume), swap_key, the path to the gpg-encrypted key, and swap_keydev, which is optional but will save you a little time during boot-up because the script won't have to search for the appropriate device. Make sure to include the ramdisk with an initrd line. Finally, you'll have to edit /etc/fstab to mount /dev/mapper/swap as the swap partition (which is what the init script will call the dmcrypt device).

During boot-up you'll see a line "TuxOnIce: Can't translate "/dev/mapper/swap" into a device id yet." but that's normal. In a few seconds the script will ask for your passphrase and then TuxOnIce will check the decrypted swap partition for a hibernation signature and is all is well!
Fight Spam! :: Valid XHTML :: Valid CSS: :: Powered by WikkaWiki