#!/usr/bin/env zsh
#
# Copyright (C) 2007-2016 Dyne.org Foundation
#
# Tomb test units by Denis Roio <jaromil@dyne.org>
#
# This source code is free software; you can redistribute it and/or
# modify it under the terms of the GNU Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This source code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Please refer
# to the GNU Public License for more details.
#
# You should have received a copy of the GNU Public License along with
# this source code; if not, write to: Free Software Foundation, Inc.,
# 675 Mass Ave, Cambridge, MA 02139, USA.


T="../../tomb"

source ${T} source
dummypass=test
dummypassnew=changetest

GLOBAL_RESULT=0

notice() { print; _message "${@}"; print; }
error() { _warning "     ${@}"; }
tt() {
	start_loops=(`sudo losetup -a |cut -d: -f1`)
	start_temps=(`find /dev/shm -name 'tomb*'`)
	${T} -D ${=@}
	res=$?
	loops=(`sudo losetup -a |cut -d: -f1`)
	temps=(`find /dev/shm -name 'tomb*'`)

	{ test "${#start_loops}" = "${#loops}" } || {
	    error "loop device usage change to ${#loops}" }
	{ test "${#start_temps}" = "${#temps}" } || {
	    error "temp files usage change to ${#temps}" }
	print "     Tomb command returns $res"
	return $res
}

# check for auxiliary programs
KDF=1
STEGHIDE=1
RESIZER=1
command -v steghide > /dev/null || STEGHIDE=0
command -v e2fsck resize2fs > /dev/null || RESIZER=0
command -v tomb-kdb-pbkdf2 > /dev/null || KDF=0
command -v qrencode > /dev/null || QRENCODE=0


typeset -A results

tests=(dig forge lock badpass open close passwd chksum bind setkey recip-dig
		recip-forge recip-lock recip-open recip-close recip-passwd recip-resize
		recip-setkey recip-default recip-hidden shared shared-passwd shared-setkey)

{ test $RESIZER = 1 } && { tests+=(resize) }
{ test $KDF = 1 } && { tests+=(kdforge kdfpass kdflock kdfopen) }
{ test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl
recip-stgin recip-stgout recip-stgopen recip-stgimpl) }
{ test $QRENCODE = 1 } && { tests+=(qrenc) }

# GnuPG Conf.
# Note: the assumption is the test keys are unencrypted.
export GNUPGHOME="gnupg/"
chmod 700 "$GNUPGHOME"
gpgid_1="A4857CD176B31435F9709D25F0E573B8289439CD"
gpgid_2="0B2235E660753AB0475FB3E23DC836481F44B31E"

notice "Loading test suite"

# functions that can be called singularly

test-tomb-create() {

    notice "wiping all test.tomb* in /tmp"
    sudo rm -f /tmp/test.tomb{,.key,.new.key}

    notice "Testing creation: dig"

    tt dig -s 20 /tmp/test.tomb

    { test $? = 0 } && { results+=(dig SUCCESS) }

    notice "Testing creation: forge"

    tt forge /tmp/test.tomb.key \
        --ignore-swap --unsafe --tomb-pwd ${dummypass}

    { test $? = 0 } && {
        results+=(forge SUCCESS)
        #
        notice "Dump of clear key contents to examine them:"
        print ${dummypass} \
	        | gpg --batch --passphrase-fd 0 --no-tty --no-options -d /tmp/test.tomb.key \
	        | xxd
        echo --
    }

    notice "Testing creation: lock"

    tt lock /tmp/test.tomb -k /tmp/test.tomb.key \
        --ignore-swap --unsafe --tomb-pwd ${dummypass}

    { test $? = 0 } && { results+=(lock SUCCESS) }
}

test-tomb-recip() {

    notice "wiping all recip.tomb* in /tmp"
    local tomb=/tmp/recip.tomb
    local tomb_key=/tmp/recip.tomb.key
    sudo rm -f "$tomb" "$tomb_key"

    notice "Testing tomb with recipient creation: dig"
    tt dig -s 20 $tomb
    { test $? = 0 } && { results+=(recip-dig SUCCESS) }

    notice "Testing tomb with recipient creation: forge"
    tt forge $tomb_key -g -r $gpgid_1 --ignore-swap --unsafe
    { test $? = 0 } && { results+=(recip-forge SUCCESS) }

    notice "Testing tomb with recipient creation: lock"
    tt lock $tomb -k $tomb_key -g -r $gpgid_1 --ignore-swap --unsafe
    { test $? = 0 } && { results+=(recip-lock SUCCESS) }

    notice "Testing tomb with recipient opening: open"
    tt open $tomb -k $tomb_key -g
    { test $? = 0 } && { results+=(recip-open SUCCESS) }

    notice "Testing tomb with recipient closing: close"
    tt close recip
    { test $? = 0 } && { results+=(recip-close SUCCESS) }

    { test $STEGHIDE = 1 } && {
        notice "Testing tomb with recipient steganographic hiding of keys"

        cp -f arditi.jpg /tmp/recip.jpg
        sudo rm -f /tmp/recip.steg.key

        tt --unsafe --tomb-pwd ${dummypass} bury -k /tmp/recip.tomb.key \
            /tmp/recip.jpg -g -r "$gpgid_1"
        { test $? = 0 } && { results+=(recip-stgin SUCCESS) }

        tt --unsafe --tomb-pwd ${dummypass} exhume -k /tmp/recip.steg.key \
            /tmp/recip.jpg
        { test $? = 0 } && { results+=(recip-stgout SUCCESS) }

        tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/recip.steg.key \
            /tmp/recip.tomb -g
        { test $? = 0 } && { results+=(recip-stgopen SUCCESS) }
        ${T} close recip

        notice "test using open -k image.jpeg"
        tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/recip.jpg \
            /tmp/recip.tomb -g
        { test $? = 0 } && { results+=(recip-stgimpl SUCCESS) }
        tt close recip
    }

	notice "Testing tomb with recipient changing gpg key: passwd"
	res=0
	tt passwd -k $tomb_key -g -r $gpgid_2
	{ test $? = 0 } || { res=1 }
	tt open $tomb -k $tomb_key -g
	{ test $? = 0 } || { res=1 }
	tt close recip
	{ test $? = 0 } || { res=1 }
	{ test $res = 0 } && { results+=(recip-passwd SUCCESS) }

	notice "Testing tomb with recipient resizing a tomb: resize"
    tt resize -s 30 $tomb -k $tomb_key -g -r $gpgid_2
    { test $? = 0 } && { results+=(recip-resize SUCCESS) }

    notice "Testing tomb with recipient setting a new key: setkey"
    sudo rm -f /tmp/new.recip.tomb.key
    res=0
    tt forge /tmp/new.recip.tomb.key -g -r $gpgid_2 \
        --ignore-swap --unsafe
    { test $? = 0 } || { res=1 }
    tt setkey -k /tmp/new.recip.tomb.key $tomb_key $tomb -g -r $gpgid_2
    { test $? = 0 } || { res=1 }
    tt open -k /tmp/new.recip.tomb.key $tomb -g
    { test $? = 0 } || { res=1 }
    { test $res = 0 } && { results+=(recip-setkey SUCCESS) }
    tt close recip
}

test-tomb-recip-default() {

	notice "wiping all default.tomb* in /tmp"
	rm -f /tmp/default.tomb /tmp/default.tomb.key /tmp/default.tmp

	notice "Testing tomb with the default recipient"
	res=0
    tt dig -s 20 /tmp/default.tomb
    { test $? = 0 } || { res=1 }
    tt forge /tmp/default.tomb.key -g --ignore-swap --unsafe
    { test $? = 0 } || { res=1 }
	tt lock /tmp/default.tomb -k /tmp/default.tomb.key \
        --ignore-swap --unsafe -g
    { test $? = 0 } || { res=1 }
    gpg -d --status-fd 2 /tmp/default.tomb.key 1> /dev/null 2> /tmp/default.tmp
    [[ -z "$(grep 'Tomb Test 2' /tmp/default.tmp)" ]] && { res=1 }
    { test $res = 0 } && { results+=(recip-default SUCCESS) }
}

test-tomb-recip-hidden() {

	notice "wiping all hidden.tomb* in /tmp"
	rm -f /tmp/hidden.tomb /tmp/hidden.tomb.key

	notice "Testing tomb with hidden recipient"
	res=0
    tt dig -s 20 /tmp/hidden.tomb
    { test $? = 0 } || { res=1 }
    tt forge /tmp/hidden.tomb.key -g -R $gpgid_1 --ignore-swap --unsafe
    { test $? = 0 } || { res=1 }
	tt lock /tmp/hidden.tomb -k /tmp/hidden.tomb.key \
        --ignore-swap --unsafe -g -R $gpgid_1
    { test $? = 0 } || { res=1 }
    { test $res = 0 } && { results+=(recip-hidden SUCCESS) }
}

test-tomb-shared() {

	notice "wiping all shared.tomb* in /tmp"
    rm -f /tmp/shared.tomb /tmp/shared.tomb.key

    notice "Testing sharing a tomb"
    res=0
    tt dig -s 20 /tmp/shared.tomb
    { test $? = 0 } || { res=1 }
    tt forge /tmp/shared.tomb.key -g -r $gpgid_1,$gpgid_2 \
        --ignore-swap --unsafe
    { test $? = 0 } || { res=1 }
    tt lock /tmp/shared.tomb -k /tmp/shared.tomb.key \
        --ignore-swap --unsafe -g -r $gpgid_1
    { test $? = 0 } || { res=1 }
    tt open /tmp/shared.tomb -k /tmp/shared.tomb.key -g
    { test $? = 0 } || { res=1 }
    tt close shared
    { test $? = 0 } || { res=1 }
    { test $res = 0 } && { results+=(shared SUCCESS) }

    notice "Testing changing recipients on a shared Tomb"
    tt passwd -k /tmp/shared.tomb.key -g -r $gpgid_2,$gpgid_1
    { test $? = 0 } && { results+=(shared-passwd SUCCESS) }

    notice "Testing setkey on a shared Tomb"
    rm -f /tmp/new.shared.tomb.key
    res=0
    tt forge /tmp/new.shared.tomb.key -g -r $gpgid_1,$gpgid_2 \
        --ignore-swap --unsafe
    { test $? = 0 } || { res=1 }
    tt setkey -k /tmp/new.shared.tomb.key /tmp/shared.tomb.key /tmp/shared.tomb \
        -g -r $gpgid_2,$gpgid_1
    { test $? = 0 } || { res=1 }
    { test $res = 0 } && { results+=(shared-setkey SUCCESS) }
}

test-bind-hooks() {
    notice "Testing bind hooks"

    tt --ignore-swap --unsafe --tomb-pwd ${dummypass} \
        open /tmp/test.tomb -k /tmp/test.tomb.key

    rnd=$RANDOM
    bindtest="dyne-tomb-bind-test-$rnd"
    echo $rnd > /media/test/$bindtest
    rm -f /media/test/bind-hooks
    echo "$bindtest $bindtest" > /media/test/bind-hooks
    touch $HOME/$bindtest
    tt close test
    tt -k /tmp/test.tomb.key --unsafe --tomb-pwd ${dummypass} open /tmp/test.tomb
    rnd2=`cat $HOME/$bindtest`
    if [ "$rnd" = "$rnd2" ]; then
	    notice "Bind hook on file matches"
	    results+=(bind SUCCESS)
	    tt list test
    else
	    error "Bind hook on file reports incongruence"
    fi
    tt close test
}

test-set-key() {

    notice "Testing set key"

    sudo rm -f /tmp/test.tomb.new.key

    tt forge -k /tmp/test.tomb.new.key --force --unsafe --tomb-pwd ${dummypass}

    tt setkey -k /tmp/test.tomb.new.key --unsafe --tomb-pwd ${dummypass} --tomb-old-pwd ${dummypass} /tmp/test.tomb.key /tmp/test.tomb

    tt open -k /tmp/test.tomb.new.key --unsafe --tomb-pwd ${dummypass} /tmp/test.tomb

    [[ $? = 0 ]] && {
        notice "Setkey successfully swapped tomb key"
        results+=(setkey SUCCESS)
        notice "Dump of clear key contents to examine them:"
        print ${dummypass} \
	        | gpg --batch --passphrase-fd 0 --no-tty --no-options -d /tmp/test.tomb.new.key \
	        | xxd
        echo --
        mv /tmp/test.tomb.new.key /tmp/test.tomb.key
        tt close test
    }
}


test-regression() {

	url=${1:-https://files.dyne.org/tomb/old-releases/Tomb-2.2.tar.gz}
	notice "Regression tests using $url"

	curl $url > /tmp/tomb-regression.tar.gz
	mkdir -p /tmp/tomb-regression
	tar xfz /tmp/tomb-regression.tar.gz \
		--strip-components 1 -C /tmp/tomb-regression

	OLDT="/tmp/tomb-regression/tomb"
	version=`${OLDT} -v |& awk 'NR==1 {print $3}'`
	_message "tomb version: $version"
	tests+=(oldnew-$version newold-$version)

	sudo rm -f /tmp/regression-test.tomb{,.key}

	${OLDT} -D dig -s 10 /tmp/regression-test.tomb
	${OLDT} -D forge /tmp/regression-test.tomb.key \
			--ignore-swap --unsafe --tomb-pwd ${dummypass}
	${OLDT} -D lock /tmp/regression-test.tomb -k /tmp/regression-test.tomb.key \
			--ignore-swap --unsafe --tomb-pwd ${dummypass}

	notice "opening old tomb and key using the new tomb"

	tt -k /tmp/regression-test.tomb.key --unsafe \
	   --tomb-pwd ${dummypass} open /tmp/regression-test.tomb

	[[ $? = 0 ]] && results+=(oldnew-$version SUCCESS)

	tt close regression-test

	notice "opening new tomb and key using the old tomb"

	${OLDT} -D -k /tmp/test.tomb.key --unsafe \
			--tomb-pwd ${dummypass} open /tmp/test.tomb

	[[ $? = 0 ]] && results+=(newold-$version SUCCESS)

	${OLDT} close test
}


test-open-read-only() {

    notice "wiping all testro.tomb* in /tmp"
    sudo rm -f /tmp/testro.tomb{,.key,.new.key}

    # Create new
    tt dig -s 20 /tmp/testro.tomb
    tt forge /tmp/testro.tomb.key \
        --ignore-swap --unsafe --tomb-pwd ${dummypass}
    tt lock /tmp/testro.tomb -k /tmp/testro.tomb.key \
        --ignore-swap --unsafe --tomb-pwd ${dummypass}

    notice "Testing open read only"

    # Remove write privilege on test.tomb
    chmod -w /tmp/testro.tomb

    # Attempt to open the unwritable tomb with the read-only mount option
    tt open /tmp/testro.tomb -k /tmp/testro.tomb.key \
        --ignore-swap --unsafe --tomb-pwd ${dummypass} -o ro,noatime,nodev

    { test $? = 0 } && {
        results+=(openro SUCCESS)
        tt close testro
    }
}


startloops=(`sudo losetup -a |cut -d: -f1`)

[[ $1 = "source" ]] && { return 0 }

[[ $1 = "" ]] || {
    tt ${=@}
    return $?
}

# isolated function (also called with source)
test-tomb-create
test-tomb-recip
test-tomb-recip-default
test-tomb-recip-hidden
test-tomb-shared

notice "Testing open with wrong password"

tt -k /tmp/test.tomb.key --unsafe --tomb-pwd wrongpassword open /tmp/test.tomb

{ test $? = 0 } || { results+=(badpass SUCCESS) }



notice "Testing open with good password"

tt -k /tmp/test.tomb.key --unsafe --tomb-pwd ${dummypass} open /tmp/test.tomb

{ test $? = 0 } && { results+=(open SUCCESS) }

tt close test

{ test $? = 0 } && { results+=(close SUCCESS) }

# isolated function
test-open-read-only


notice "Testing changing tomb password"

tt passwd /tmp/test.tomb \
    -k /tmp/test.tomb.key --unsafe --tomb-old-pwd ${dummypass} --tomb-pwd ${dummypassnew}

tt passwd /tmp/test.tomb \
    -k /tmp/test.tomb.key --unsafe --tomb-old-pwd ${dummypassnew} --tomb-pwd ${dummypass}

{ test $? = 0 } && { results+=(passwd SUCCESS) }





notice "Generating content for file integrity test"

tt -k /tmp/test.tomb.key --unsafe --tomb-pwd ${dummypass} open /tmp/test.tomb

tt dig -s 10 /media/test/datacheck.raw

crc="sha256 /media/test/datacheck.raw"
echo "$crc" > /media/test/datacheck.sha

tt --unsafe close test

{ test $RESIZER = 1 } && {
    notice "Testing resize to 30 MiB"

    tt --unsafe --tomb-pwd ${dummypass} -k /tmp/test.tomb.key resize /tmp/test.tomb -s 30

    { test $? = 0 } && { results+=(resize SUCCESS) }

}

notice "Testing contents integrity"

tt -k /tmp/test.tomb.key --unsafe --tomb-pwd ${dummypass} open /tmp/test.tomb

{ test $? = 0 } && {

    crc2="sha256 /media/test/datacheck.raw"

    { test "$crc" = "$crc2" } && { results+=(chksum SUCCESS) }

    tt close test
}


# regression tests with previous stable versions
test-regression https://files.dyne.org/tomb/old-releases/Tomb-2.2.tar.gz
test-regression https://files.dyne.org/tomb/old-releases/Tomb-2.1.1.tar.gz
test-regression https://files.dyne.org/tomb/old-releases/Tomb-2.0.1.tar.gz


# isolated function
test-bind-hooks









# iso func
test-set-key




{ test $KDF = 1 } && {

    notice "Testing KDF key"
    sudo rm -f /tmp/test.tomb.kdf /tmp/kdf.tomb

    tt --unsafe --tomb-pwd ${dummypass}  --kdf 1 forge -k /tmp/test.tomb.kdf

    { test $? = 0 } && { results+=(kdforge SUCCESS) }

    tt passwd --unsafe --tomb-old-pwd ${dummypass} --tomb-pwd ${dummypassnew} --kdf 1 -k /tmp/test.tomb.kdf

    { test $? = 0 } && { results+=(kdfpass SUCCESS) }

    tt dig -s 10 /tmp/kdf.tomb

    tt lock /tmp/kdf.tomb -k /tmp/test.tomb.kdf \
        --ignore-swap --unsafe --tomb-pwd ${dummypassnew} --kdf 1

    { test $? = 0 } && { results+=(kdflock SUCCESS) }

    tt open /tmp/kdf.tomb -k /tmp/test.tomb.kdf \
        --ignore-swap --unsafe --tomb-pwd ${dummypassnew} --kdf 1

    { test $? = 0 } && { results+=(kdfopen SUCCESS) }

    ${T} close kdf

}

{ test $STEGHIDE = 1 } && {

    notice "Testing steganographic hiding of keys"

    cp -f arditi.jpg /tmp/tomb.jpg
    sudo rm -f /tmp/test.steg.key

    tt --unsafe --tomb-pwd ${dummypass} bury -k /tmp/test.tomb.key /tmp/tomb.jpg

    { test $? = 0 } && { results+=(stgin SUCCESS) }

    rm -f /tmp/test.steg.key

    tt --unsafe --tomb-pwd ${dummypass} exhume -k /tmp/test.steg.key /tmp/tomb.jpg

    { test $? = 0 } && { results+=(stgout SUCCESS) }

    tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/test.steg.key /tmp/test.tomb

    { test $? = 0 } && { results+=(stgopen SUCCESS) }

    ${T} close test

    # test piping keys using -k -
    tkey=`tt --unsafe --tomb-pwd ${dummypass} exhume /tmp/tomb.jpg`
    print "$tkey" | tt --unsafe --tomb-pwd ${dummypass} open -k - /tmp/test.tomb
    { test $? = 0 } && { results+=(stgpipe SUCCESS) }

    ${T} close test


    notice "test using open -k image.jpeg"

    tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/tomb.jpg /tmp/test.tomb
    { test $? = 0 } && { results+=(stgimpl SUCCESS) }

    tt close test
}

{ test $QRENCODE = 1 } && {

    notice "test rendering a QR printable key backup"

    tt engrave -k /tmp/test.tomb.key

    { test $? = 0 } && { results+=(qrenc SUCCESS) }

}

# rm /tmp/test.tomb{,.key} -f || exit 1

endloops=(`sudo losetup -a |cut -d: -f1`)

notice "Test results summary"

print "${#startloops} loop devices busy at start"

for t in $tests; do
    res=${results[$t]:-FAIL}
    [[ "$res" == "SUCCESS" ]] || GLOBAL_RESULT=1
    echo "$t\t$res"
done

print "${#endloops} loop devices busy at end"
print "Done. You can remove temporary leftovers from /tmp :"
for i in `find /tmp -name '*tomb*' 2>/dev/null`; do ls -lh $i; done
return $GLOBAL_RESULT
