Jan-Philipp Litza

Opening TRESOR without TRESOR 23. April 2012

I spent the last few hours toying around with TRESOR, a fairly new approach to encrypt disks in Linux without storing the key in RAM, thus avoiding cold boot attacks and the like.

After an initial benchmark test of how bad the performance gets (not much worse than with LUKS and AES. That is, on an AES-NI-capable Core i5), I started thinking about disaster recovery: What if my /boot partition dies or anything else happens that prevents me from booting a TRESOR-capable kernel. How am I going to access my data without compiling another kernel?

The way you open the encrypted device is just the same as for normal dm-crypt devices, with cryptsetup. Only the --cipher tresor option you pass it is special about TRESOR devices. So I started digging around the TRESOR patch to find out how the password that is asked on startup is treated: It is iterated „2000“ times through SHA256, as their paper states.

Well, it cost me many hours to find out the little quirks about that:

As there is no way to specifiy iteration counts (as we would need it), I implemented the iterated hashing myself by first copying parts of the original patch into a standalone C program and after verifying it actually works, hacking together a bash script to do the same thing. While this script takes tens of thousands times longer than the C program (literally! Nearly 20 seconds compared to instant), it doesn’t need compilation and runs on nearly any system with basic tools like sha256sum, sed and bash.

So the bottom line is: If you ever need to mount a TRESOR volume without having a TRESOR-patched kernel at hand, use the following little script instead, either to generate the keyfile you pass to cryptsetup or to directly call it:

#!/bin/bash

help() {
    echo "Usage: $0 [--mount <options>]"
    echo
    echo "  --mount <options>   call cryptsetup with the generated key"
    echo "                      and optionally further <options>"
    exit 0
}

case "$1" in
  --mount)      ;;
  --help)       help ;;
  -h)           help ;;
  -?)           help ;;
  *)
      exec 3>&1
      exec >&2
      ;;
esac

correct="no"

while [ "$correct" != "yes" ] ; do
    echo
    echo " >> TRESOR <<"
    echo
    read -rsp " Enter password         > " cur
    echo "(computing key, this might take a while)"

    for i in $(seq 1 4001); do
        cur="$(printf "%b" "$cur" | sha256sum -b | sed -e 's/\s.*$//' -e 's/\(..\)/\\x\1/g')"
    done
    hashed="$(printf "%b" "$cur" | sha256sum -b | sed -e 's/\s.*$//' -e 's/\(..\)/\1 /g')"
    hashed1="$(echo "$hashed" | sed -e 's/^\(.\{47\}\) \(.\{47\}\)/\1/')"
    hashed2="$(echo "$hashed" | sed -e 's/^\(.\{47\}\) \(.\{47\}\)/\2/')"
    echo
    echo " Confirm key hash       > ${hashed1}"
    echo "                          ${hashed2}"
    echo

    read -p " Correct (yes/no)       > " correct
done

if [ "$1" = "--mount" ] ; then
    shift
    printf "%b" "$cur" | cryptsetup --cipher aes-cbc-plain --key-size 256 --key-file=/dev/stdin create "$@"
else
    printf "%b" "$cur" >&3
fi