commit 8e9fc7e8030a8bf9305f7ca92e18fb3bb41c90ef
parent 4d6c0bf5cc0502357bfe59c7282a73662909a172
Author: Jaromil <jaromil@dyne.org>
Date: Wed, 12 Jun 2013 13:33:54 +0200
Major fixes to KDF and steganography
With the advent of a proper test suite many bugs were found and
squashed both in the way KDF and steghide were used.
Key validation func is_valid_key() now attempts recovery for keys
that have broken headers or are naked text (back-compat to old exhume).
KDF and steg now work correctly.
Diffstat:
M | tomb | | | 286 | +++++++++++++++++++++++++++++++++++++------------------------------------------- |
1 file changed, 132 insertions(+), 154 deletions(-)
diff --git a/tomb b/tomb
@@ -48,6 +48,7 @@ for arg in ${argv}; do OLDARGS+=($arg); done
DD="dd"
WIPE="rm -f"
MKFS="mkfs.ext3 -q -F -j -L"
+KDF=1
STEGHIDE=1
MKTEMP=1
RESIZER=1
@@ -65,8 +66,8 @@ typeset -h _gid
typeset -h _tty
# Set a sensible PATH
-PATH=/sbin:/bin:/usr/sbin:/usr/bin
-[[ "$TOMBEXEC" =~ "^/usr/local" ]] && PATH="/usr/local/bin:/usr/local/sbin:$PATH"
+# PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
+
# }}}
@@ -104,9 +105,9 @@ safe_dir() {
if _have_shm; then
xxx "safe_dir creating $1 dir in RAM"
if (( $MKTEMP )); then
- mktemp -d /dev/shm/$1.$$.XXXXXXX
+ mktemp -d /dev/shm/tomb.$1.$$.XXXXXXX
else
- dir="/dev/shm/$1.$$.$RANDOM$RANDOM"
+ dir="/dev/shm/tomb.$1.$$.$RANDOM$RANDOM"
mkdir -m 0700 -p "$dir"
print "$dir"
fi
@@ -125,8 +126,8 @@ safe_dir() {
safe_filename() {
_have_shm || die "No access to shared memory on this system, sorry."
(( $MKTEMP )) && \
- mktemp -u /dev/shm/$1.$$.XXXXXXX || \
- print "/dev/shm/$1.$$.$RANDOM$RANDOM"
+ mktemp -u /dev/shm/tomb.$1.$$.XXXXXXX || \
+ print "/dev/shm/tomb.$1.$$.$RANDOM$RANDOM"
}
# Check if swap is activated
@@ -267,6 +268,14 @@ Options:
-n don't process the hooks found in tomb
-o mount options used to open (default: rw,noatime,nodev)
-f force operation (i.e. even if swap is active)
+EOF
+ { test "$KDF" = 1 } && {
+ cat <<EOF
+ --kdf seconds generate passwords against dictionary attacks
+EOF
+ }
+
+cat <<EOF
-h print this help
-v print version, license and list of available ciphers
@@ -419,17 +428,8 @@ check_bin() {
# check for resize
command -v e2fsck resize2fs > /dev/null || RESIZER=0
- if which tomb-kdf-pbkdf2 &> /dev/null; then
- KDF_PBKDF2="tomb-kdf-pbkdf2"
- else
- local our_pbkdf2
- our_pbkdf2="$(dirname $(readlink -f $TOMBEXEC))/kdf/tomb-kdf-pbkdf2"
- if which $our_pbkdf2 &> /dev/null; then
- KDF_PBKDF2=$our_pbkdf2
- else
- KDF_PBKDF2=
- fi
- fi
+ # check for KDF auxiliary tools
+ command -v tomb-kdb-pbkdf2 > /dev/null || KDF=0
}
@@ -451,7 +451,7 @@ load_key() {
if [[ "`option_value -k`" == "-" ]]; then
xxx "load_key reading from stdin"
# take key from stdin
- tombkeydir=`safe_dir tomb`
+ tombkeydir=`safe_dir load_key`
xxx "tempdir is $tombkeydir"
cat > ${tombkeydir}/stdin.tmp
tombdir=${tombkeydir}
@@ -551,8 +551,8 @@ change_passwd() {
local tmpnewkey lukskey c tombpass tombpasstmp
- tmpnewkey=`safe_filename tombnew`
- lukskey=`safe_filename tombluks`
+ tmpnewkey=`safe_filename passnew`
+ lukskey=`safe_filename passold`
_success "Changing password for $keyfile"
@@ -594,9 +594,34 @@ drop_key() {
#$1 is the keyfile we are checking
is_valid_key() {
# this header validity check is a virtuosism by Hellekin
- [[ `file =(awk '/^-+BEGIN/,0' $1) -bi` =~ application/pgp ]]
- return $?
+ [[ `file =(awk '/^-+BEGIN/,0' $1)` =~ PGP ]] && return 0
+ # if no BEGIN header found then we try to recover it
+ [[ `file $1 -bi` =~ text/plain ]] && {
+ _warning "Key data found with missing headers, attempting recovery"
+ local tmp_keyfix=`safe_filename keyfix`
+ touch $tmp_keyfix
+ # make sure KDF header comes first
+ local header=`grep '^_KDF_' $1`
+ print "$header" >> $tmp_keyfix
+ cat $1 | awk '
+BEGIN {
+print "-----BEGIN PGP MESSAGE-----"
+print
}
+/^_KDF_/ { next }
+{ print $0 }
+END {
+print "-----END PGP MESSAGE-----"
+}' >> ${tmp_keyfix}
+ mv $tmp_keyfix $1
+ chown ${_uid}:${_gid} ${1}
+ chmod 0600 ${1}
+ return 0
+ }
+ _warning "Invalid key format: $1"
+ return 1
+}
+
# Gets a key file and a password, prints out the decoded contents to
# be used directly by Luks as a cryptographic key
@@ -611,11 +636,8 @@ get_lukskey() {
_verbose "KDF: `cut -d_ -f 3 <<<$firstline`"
case `cut -d_ -f 3 <<<$firstline` in
pbkdf2sha1)
- if [[ -z $KDF_PBKDF2 ]]; then
- die "The tomb use kdf method 'pbkdf2', which is unsupported on your system"
- fi
pbkdf2_param=`cut -d_ -f 4- <<<$firstline | tr '_' ' '`
- tombpass=$(${KDF_PBKDF2} ${=pbkdf2_param} 2> /dev/null <<<$tombpass)
+ tombpass=$(tomb-kdb-pbkdf2 ${=pbkdf2_param} 2> /dev/null <<<$tombpass)
;;
*)
_failure "No suitable program for KDF `cut -f 3 <<<$firstline`"
@@ -628,26 +650,26 @@ get_lukskey() {
# fix for gpg 1.4.11 where the --status-* options don't work ;^/
gpgver=`gpg --version | awk '/^gpg/ {print $3}'`
if [ "$gpgver" = "1.4.11" ]; then
- xxx "GnuPG is version 1.4.11 - adopting status fix"
-
- print ${tombpass} | \
- gpg --batch --passphrase-fd 0 --no-tty --no-options -d "${keyfile}"
- unset tombpass
- ret=$?
-
+ xxx "GnuPG is version 1.4.11 - adopting status fix"
+
+ print ${tombpass} | \
+ gpg --batch --passphrase-fd 0 --no-tty --no-options -d "${keyfile}"
+ ret=$?
+ unset tombpass
+
else # using status-file in gpg != 1.4.12
- res=`safe_filename tomb.open`
- { test $? = 0 } || { unset tombpass; die "Fatal error creating temp file." }
-
- print ${tombpass} | \
- gpg --batch --passphrase-fd 0 --no-tty --no-options --status-fd 2 \
- --no-mdc-warning --no-permission-warning --no-secmem-warning \
- -d "${keyfile}" 2>$res
- unset tombpass
- grep 'DECRYPTION_OKAY' $res
- ret=$?; rm -f $res
-
+ res=`safe_filename lukskey`
+ { test $? = 0 } || { unset tombpass; die "Fatal error creating temp file." }
+
+ print ${tombpass} | \
+ gpg --batch --passphrase-fd 0 --no-tty --no-options --status-fd 2 \
+ --no-mdc-warning --no-permission-warning --no-secmem-warning \
+ -d "${keyfile}" 2>$res
+ unset tombpass
+ grep 'DECRYPTION_OKAY' $res
+ ret=$?; rm -f $res
+
fi
xxx "get_lukskey returns $ret"
return $ret
@@ -689,42 +711,25 @@ gen_key() {
xxx "gen_key takes tombpass from CLI argument: $tombpass"
fi
-
+ header=""
+ { option_is_set --kdf } && {
# KDF is a new key strenghtening technique against brute forcing
# see: https://github.com/dyne/Tomb/issues/82
- _verbose "KDF method chosen is: '`option_value --kdf`'"
- kdf_method=$(cut -d: -f1 <<<`option_value --kdf` )
- case $kdf_method in
- pbkdf2)
- if [[ -z $KDF_PBKDF2 ]]; then
- die "The tomb use kdf method 'pbkdf2', which is unsupported on your system"
- fi
+ itertime="`option_value --kdf`"
+ _verbose "KDF itertime chosen: $itertime"
# --kdf takes one parameter: iter time (on present machine) in seconds
- seconds=$(cut -d: -f2 -s <<<`option_value --kdf`)
- if [[ -z $seconds ]]; then
- seconds=1
- fi
- local -i microseconds
- microseconds=$((seconds*1000000))
- _verbose "Microseconds: $microseconds"
- pbkdf2_salt=`${KDF_PBKDF2}-gensalt`
- pbkdf2_iter=`${KDF_PBKDF2}-getiter $microseconds`
+ local -i microseconds
+ microseconds=$((itertime*1000000))
+ _verbose "Microseconds: $microseconds"
+ pbkdf2_salt=`tomb-kdb-pbkdf2-gensalt`
+ pbkdf2_iter=`tomb-kdb-pbkdf2-getiter $microseconds`
# We use a length of 64bytes = 512bits (more than needed!?)
- tombpass=`${KDF_PBKDF2} $pbkdf2_salt $pbkdf2_iter 64 <<<"${tombpass}"`
-
- header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n"
- ;;
- ""|null)
+ tombpass=`tomb-kdb-pbkdf2 $pbkdf2_salt $pbkdf2_iter 64 <<<"${tombpass}"`
+
+ header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n"
+ }
- header=""
- ;;
- *)
- _warning "KDF method non recognized"
- return 1
- header=""
- ;;
- esac
- echo -n $header
+ print -n $header
print "${tombpass}" \
| gpg --openpgp --batch --no-options --no-tty --passphrase-fd 0 2>/dev/null \
@@ -749,15 +754,14 @@ BEGIN { ciphers=0 }
}
# Steganographic function to bury a key inside an image.
+# Requires steghide(1) to be installed
bury_key() {
- tombkey=$1
- imagefile=$2
+ tombkey="`option_value -k`"
+ imagefile=$1
+
+ { is_valid_key ${tombkey} } || {
+ die "Bury failed: not a tomb key $tombkey" }
- file $tombkey | grep PGP > /dev/null
- if [ $? != 0 ]; then
- _warning "encode failed: $tombkey is not a tomb key"
- return 1
- fi
file $imagefile | grep JPEG > /dev/null
if [ $? != 0 ]; then
_warning "encode failed: $imagefile is not a jpeg image"
@@ -765,27 +769,16 @@ bury_key() {
fi
_success "Encoding key $tombkey inside image $imagefile"
- _message "please choose a password for the encoding"
-
- # here user is prompted for key password
- for c in 1 2 3; do
- # 3 tries to write two times a matching password
- tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${tombkey}"`
- tombpasstmp=$tombpass
- tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${tombkey} (again)"`
- if [ "$tombpasstmp" = "$tombpass" ]; then
- break;
- fi
- unset tombpasstmp
- unset tombpass
- done
+ _message "please confirm the key password for the encoding"
+ tombpass=`ask_key_password $tombkey`
+ { test $? = 0 } || {
+ _warning "Wrong password supplied."
+ die "You shall not bury a key whose password is unknown to you."
+ }
- if [ -z $tombpass ]; then
- _warning "passwords don't match, aborting operation."
- return 1
- fi
+ # we omit armor strings since having them as constants can give
+ # ground to effective attacks on steganography
-# Requires steghide to be installed
awk '
/^-----/ {next}
/^Version/ {next}
@@ -806,8 +799,8 @@ bury_key() {
}
# Steganographic function to exhume a key buries into an image
exhume_key() {
- tombname=$1
- imagefile=$2
+ tombkey="`option_value -k`"
+ imagefile=$1
res=1
file $imagefile | grep JPEG > /dev/null
@@ -816,44 +809,35 @@ exhume_key() {
return 1
fi
- keyfile=${tombname%%\.*}.tomb.key
- if [[ -e "$keyfile" ]]; then
- _warning "Key file $keyfile already exist."
- return 1
+ if [[ -e "$tombkey" ]]; then
+ _warning "File exists: $tombkey"
+ { option_is_set -f } || {
+ _warning "Make explicit use of --force to overwrite"
+ die "Refusing to overwrite file. Operation aborted." }
+ _warning "Use of --force selected: overwriting."
+ rm -f ${tombkey}
fi
- _message "Trying to exhume a key out of image $imagefile"
- for c in 1 2 3; do
- if [ $c = 1 ]; then
- tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${keyfile}"`
- else
- tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for $keyfile (retry $c)"`
- fi
+ _message "Trying to exhume a key out of image $imagefile"
+ if option_is_set --tomb-pwd; then
+ tombpass=`option_value --tomb-pwd`
+ xxx "ask_key_password takes tombpass from CLI argument: $tombpass"
+ else
+ tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${tombkey}"`
+ fi
# always steghide required
- steghide extract -sf ${imagefile} -p ${tombpass} -xf - \
- | awk '
-BEGIN {
-print "-----BEGIN PGP MESSAGE-----"
-}
-{ print $0 }
-END {
-print "-----END PGP MESSAGE-----"
-}' > ${keyfile}
-
- if [ "`cat ${keyfile} | wc -l`" != "3" ]; then
- _success "${keyfile} succesfully decoded"
- res=0
- break;
- fi
- done
+ steghide extract -sf ${imagefile} -p ${tombpass} -xf ${tombkey}
+ res=$?
unset tombpass
-
- if [ $res != 0 ]; then
- _warning "nothing found."
+
+ if [ $res = 0 ]; then
+ _success "${tombkey} succesfully decoded"
+ return 0
fi
- return $res
+ _warning "nothing found in $imagefile"
+ return 1
}
# }}} - Key handling
@@ -885,7 +869,7 @@ forge_key() {
# create the keyfile in tmpfs so that we leave less traces in RAM
- keytmp=`safe_dir tomb`
+ keytmp=`safe_dir forge`
(( $? )) && die "error creating temp dir"
xxx "safe_dir at $keytmp"
@@ -1646,7 +1630,7 @@ resize_tomb() {
die "Aborting operations: error loading key $tombkey" }
# make sure to call drop_key later
- local tmp_resize=`safe_filename tmbrsz`
+ local tmp_resize=`safe_filename resize`
local newtombsize=$opts[-s]
local oldtombsize=$(( `stat -c %s "$1" 2>/dev/null` / 1048576 ))
local mounted_tomb=`mount -l |
@@ -1892,10 +1876,10 @@ main() {
subcommands_opts[open]="f n -nohook=n k: -key=k o: -mount-options=o -ignore-swap -sudo-pwd: -tomb-pwd:"
subcommands_opts[mount]=${subcommands_opts[open]}
- subcommands_opts[create]="f -force -ignore-swap s: -size=s k: -key=k -kdf: -sudo-pwd: -tomb-pwd: -use-urandom"
+ subcommands_opts[create]="" # deprecated, will issue warning
subcommands_opts[forge]="f -force -ignore-swap k: -key=k -kdf: -tomb-pwd: -use-urandom"
- subcommands_opts[dig]="f -forge -ignore-swap s: -size=s"
+ subcommands_opts[dig]="f -force -ignore-swap s: -size=s"
subcommands_opts[lock]="f -force -ignore-swap s: -size=s k: -key=k -sudo-pwd: -tomb-pwd:"
subcommands_opts[passwd]="f -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: "
@@ -1908,8 +1892,8 @@ main() {
subcommands_opts[search]=""
subcommands_opts[help]=""
- subcommands_opts[bury]=""
- subcommands_opts[exhume]=""
+ subcommands_opts[bury]="f -force k: -key=k -tomb-pwd:"
+ subcommands_opts[exhume]="f -force k: -key=k -tomb-pwd:"
subcommands_opts[decompose]=""
subcommands_opts[recompose]=""
subcommands_opts[install]=""
@@ -2055,24 +2039,18 @@ main() {
usage
;;
bury)
- if [ "$STEGHIDE" = 0 ]; then
- _warning "steghide not installed. Cannot bury your key"
- return 1
- fi
- bury_key $PARAM[1] $PARAM[2]
+ { test "$STEGHIDE" = 0 } && {
+ die "Steghide not installed: cannot bury keys into images." }
+ bury_key $PARAM[1]
;;
exhume)
- if [ "$STEGHIDE" = 0 ]; then
- _warning "steghide not installed. Cannot exhume your key"
- return 1
- fi
- exhume_key $PARAM[1] $PARAM[2]
+ { test "$STEGHIDE" = 0 } && {
+ die "Steghide not installed: cannot exhume keys from images." }
+ exhume_key $PARAM[1]
;;
resize)
- if [ "$RESIZER" = 0 ]; then
- _warning "resize2fs not installed. Cannot resize your tomb."
- return 1
- fi
+ { test "$RESIZER" = 0 } && {
+ die "Resize2fs not installed: cannot resize tombs." }
check_priv
resize_tomb $PARAM[1]
;;