#!/bin/bash
#
# CAVS test executor
# Written and Copyright (c) by: Stephan Müller <smueller@chronox.de>
#
# License: see LICENSE file
#
#                            NO WARRANTY
#
#    BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
#    FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
#    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
#    PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
#    OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
#    TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
#    PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
#    REPAIR OR CORRECTION.
#
#    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
#    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
#    REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
#    INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
#    OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
#    TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
#    YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
#    PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
#    POSSIBILITY OF SUCH DAMAGES.

# Directory structure
# testvectors/
#	aesasm/
#		AES/
#		AES_GCM/
#		...
#	aesni
#		AES/
#		...
#	shassse3
#		SHA/
#		HMAC/
#		DRBG800-90A/
#	remain
#		TDES/
#		...
TESTDIR="testvectors"

# check whether a given kernel version is present
# returns true for yes, false for no
check_min_kernelver() {
	major=$1
	minor=$2

	local our_major=$(uname -r | cut -d"." -f1)

	if [ $our_major -gt $major ]; then
		return 0
	elif [ $our_major -ge $major ]; then
		if [ $(uname -r | cut -d"." -f2) -ge $minor ]; then
			return 0
		fi
	fi
	return 1
}

if [ $(uname -m) = "i686" ]; then
	EXEC="aesasm aesasm_iiv aesasm_eiv aesgen aesgen_iiv aesgen_eiv shagen remain"

elif [ $(uname -m) = "s390x" ]; then
	EXEC="s390x_cpacfc s390x_cpacfc_iiv s390x_cpacfc_eiv s390x_cpacfasm s390x_cpacfasm_iiv s390x_cpacfasm_eiv aesgen aesgen_iiv aesgen_eiv shagen remain"
else
	EXEC="aesasm aesasm_iiv aesasm_eiv aesni aesni_iiv aesni_eiv aesni_blkasm aesni_blkasm_iiv aesgen aesgen_iiv aesgen_eiv shagen shassse3 x86tdesblkasm x86tdesasm remain"

	# Intel SHA implementations are now directly callable
	if $(check_min_kernelver 4 4); then
		if $(grep -q avx /proc/cpuinfo) ; then
			EXEC="$EXEC shaavx"
		fi
		if $(grep -q avx2 /proc/cpuinfo) ; then
			EXEC="$EXEC shaavx2"
		fi
	fi

	if $(check_min_kernelver 4 8); then
		if $(grep -q avx2 /proc/cpuinfo) ; then
			EXEC="$EXEC shamb"
		fi
	elif $(check_min_kernelver 3 18); then
		if $(grep -q avx2 /proc/cpuinfo) ; then
			EXEC="$EXEC shamb"
		fi
	fi

	if $(check_min_kernelver 4 11); then
		EXEC="$EXEC aesti aesti_iiv aesti_eiv"
	fi
fi

iiv_name() {
	name=$1

	# New name with 4.2
	if $(check_min_kernelver 4 2); then
		name="seqiv($name)"
	fi

	echo $name
}

xts_name() {
	name=$1

	# New with 4.9
	if $(check_min_kernelver 4 9); then
		name="xts(ecb($name))"
	else
		name="xts($name)"
	fi

	echo $name
}

cbcmac_name() {
	name=$1

	# New with 4.11
	if $(check_min_kernelver 4 11); then
		name="cbcmac($name)"
	else
		name="$name"
	fi

	echo $name
}

################### AES TESTS #######################
AES_TESTS="AES AES_GCM CCM CMAC XTS DRBG800-90A KeyWrap38F"
#AES_TESTS="AES AES_GCM CCM CMAC XTS DRBG800-90A KeyWrap38F KDF800-108"

# x86 Assembler cipher and C block chaining modes
CIPHER_CALL_aesasm="KCAPI_GCM_AES=\"gcm_base(ctr(aes-asm),ghash-generic)\" \
		KCAPI_CCM_AES=\"ccm_base(ctr(aes-asm),$(cbcmac_name "aes-asm"))\" \
		KCAPI_CBC_AES=\"cbc(aes-asm)\" \
		KCAPI_ECB_AES=\"ecb(aes-asm)\" \
		KCAPI_XTS_AES=\"$(xts_name "aes-asm")\" \
		KCAPI_KW_AES=\"kw(aes-asm)\" \
		KCAPI_CMAC_AES=\"cmac(aes-asm)\" \
		cavs_driver"
CIPHER_TESTS_aesasm=$AES_TESTS
CIPHER_CALL_aesasm_iiv="KCAPI_CBC_AES=\"cbc(aes-asm)\" \
		KCAPI_ECB_AES=\"ecb(aes-asm)\" \
		KCAPI_GCM_AES=\"$(iiv_name "rfc4106(gcm_base(ctr(aes-asm),ghash-generic))")\" \
		cavs_driver";
CIPHER_TESTS_aesasm_iiv="AES AES_GCM"
CIPHER_CALL_aesasm_eiv="KCAPI_CBC_AES=\"cbc(aes-asm)\" \
		KCAPI_ECB_AES=\"ecb(aes-asm)\" \
		KCAPI_GCM_AES=\"rfc4106(gcm_base(ctr(aes-asm),ghash-generic))\" \
		cavs_driver";
CIPHER_TESTS_aesasm_eiv="AES AES_GCM"

# s390 CPACF assembler cipher and C block chaining modes
CIPHER_CALL_s390x_cpacfc="KCAPI_GCM_AES=\"gcm_base(ctr(aes-s390),ghash-generic)\" \
		KCAPI_CCM_AES=\"ccm_base(ctr(aes-s390),$(cbcmac_name "aes-s390"))\" \
		KCAPI_CBC_AES=\"cbc(aes-s390)\" \
		KCAPI_ECB_AES=\"ecb(aes-s390)\" \
		KCAPI_XTS_AES=\"$(xts_name "aes-s390")\" \
		KCAPI_KW_AES=\"kw(aes-s390)\" \
		KCAPI_CMAC_AES=\"cmac(aes-s390)\" \
		KCAPI_CBC_TDES=\"cbc(des3_ede-s390)\" \
		KCAPI_CTR_TDES=\"ctr(des3_ede-s390)\" \
		KCAPI_ECB_TDES=\"ecb(des3_ede-s390)\" \
		KCAPI_CMAC_TDES=\"cmac(des3_ede-s390)\" \
		KCAPI_SHA1=\"sha1-s390\" \
		KCAPI_SHA224=\"sha224-s390\" \
		KCAPI_SHA256=\"sha256-s390\" \
		KCAPI_SHA384=\"sha384-s390\" \
		KCAPI_SHA512=\"sha512-s390\" \
		cavs_driver"
CIPHER_TESTS_s390x_cpacfc="$AES_TESTS TDES CMAC SHA HMAC DRBG800-90A RSA2"
CIPHER_CALL_s390x_cpacfc_iiv="KCAPI_CBC_AES=\"cbc(aes-s390)\" \
		KCAPI_ECB_AES=\"ecb(aes-s390)\" \
		KCAPI_GCM_AES=\"$(iiv_name "rfc4106(gcm_base(ctr-aes-s390,ghash-generic))")\" \
		cavs_driver";
CIPHER_TESTS_s390x_cpacfc_iiv="AES AES_GCM"
CIPHER_CALL_s390x_cpacfc_eiv="KCAPI_CBC_AES=\"cbc(aes-s390)\" \
		KCAPI_ECB_AES=\"ecb(aes-s390)\" \
		KCAPI_GCM_AES=\"rfc4106(gcm_base(ctr-aes-s390,ghash-generic))\" \
		cavs_driver";
CIPHER_TESTS_s390x_cpacfc_eiv="AES AES_GCM"

# s390 CPACF assembler cipher and assembler block chaining modes
CIPHER_CALL_s390x_cpacfasm="KCAPI_GCM_AES=\"gcm_base(ctr-aes-s390,ghash-s390)\" \
		KCAPI_CCM_AES=\"ccm_base(ctr-aes-s390,$(cbcmac_name "aes-s390"))\" \
		KCAPI_CBC_AES=\"cbc-aes-s390\" \
		KCAPI_ECB_AES=\"ecb-aes-s390\" \
		KCAPI_XTS_AES=\"xts-aes-s390\" \
		KCAPI_CBC_TDES=\"cbc-des3_ede-s390\" \
		KCAPI_ECB_TDES=\"ecb-des3_ede-s390\" \
		KCAPI_CTR_TDES=\"ctr-des3_ede-s390\" \
		cavs_driver"
CIPHER_TESTS_s390x_cpacfasm="AES AES_GCM CCM XTS DRBG800-90A TDES"
CIPHER_CALL_s390x_cpacfasm_iiv="KCAPI_CBC_AES=\"cbc-aes-s390\" \
		KCAPI_ECB_AES=\"ecb-aes-s390\" \
		KCAPI_GCM_AES=\"$(iiv_name "rfc4106(gcm_base(ctr-aes-s390,ghash-s390))")\" \
		cavs_driver";
CIPHER_TESTS_s390x_cpacfasm_iiv="AES AES_GCM"
CIPHER_CALL_s390x_cpacfasm_eiv="KCAPI_CBC_AES=\"cbc-aes-s390\" \
		KCAPI_ECB_AES=\"ecb-aes-s390\" \
		KCAPI_GCM_AES=\"rfc4106(gcm_base(ctr-aes-s390,ghash-s390))\" \
		cavs_driver";
CIPHER_TESTS_s390x_cpacfasm_eiv="AES AES_GCM"

# AESNI cipher and C block chaining modes
CIPHER_CALL_aesni="KCAPI_GCM_AES=\"gcm_base(ctr-aes-aesni,ghash-clmulni)\" \
		KCAPI_CCM_AES=\"ccm_base(ctr-aes-aesni,$(cbcmac_name "aes-aesni"))\" \
		KCAPI_CBC_AES=\"cbc(aes-aesni)\" \
		KCAPI_ECB_AES=\"ecb(aes-aesni)\" \
		KCAPI_XTS_AES=\"$(xts_name "aes-aesni")\" \
		KCAPI_KW_AES=\"kw(aes-aesni)\" \
		KCAPI_CMAC_AES=\"cmac(aes-aesni)\" \
		cavs_driver"
if [ $(uname -m) = "i686" ]; then
	CIPHER_TESTS_aesni="AES CMAC XTS DRBG800-90A"
else
	CIPHER_TESTS_aesni=$AES_TESTS
fi
CIPHER_CALL_aesni_iiv="KCAPI_CBC_AES=\"cbc(aes-aesni)\" \
		KCAPI_ECB_AES=\"ecb(aes-aesni)\" \
		KCAPI_GCM_AES=\"$(iiv_name "rfc4106(gcm_base(ctr(aes-aesni),ghash-clmulni))")\" \
		cavs_driver";
CIPHER_TESTS_aesni_iiv="AES AES_GCM"
CIPHER_CALL_aesni_eiv="KCAPI_CBC_AES=\"cbc(aes-aesni)\" \
		KCAPI_ECB_AES=\"ecb(aes-aesni)\" \
		KCAPI_GCM_AES=\"rfc4106(gcm_base(ctr(aes-aesni),ghash-clmulni))\" \
		cavs_driver";
CIPHER_TESTS_aesni_eiv="AES AES_GCM"

# AESNI cipher and assembler block chaining modes
CIPHER_CALL_aesni_blkasm="KCAPI_GCM_AES=\"rfc4106-gcm-aesni\" \
		KCAPI_CBC_AES=\"cbc-aes-aesni\" \
		KCAPI_ECB_AES=\"ecb-aes-aesni\" \
		KCAPI_XTS_AES=\"xts-aes-aesni\" \
		cavs_driver";
if [ $(uname -m) = "i686" ]; then
	CIPHER_TESTS_aesni_blkasm="AES"
else
	CIPHER_TESTS_aesni_blkasm="AES AES_GCM XTS"
fi

# AESNI GCM tests with internal IV generation
CIPHER_CALL_aesni_blkasm_iiv="KCAPI_CBC_AES=\"cbc-aes-aesni\" \
		KCAPI_ECB_AES=\"ecb-aes-aesni\" \
		KCAPI_GCM_AES=\"$(iiv_name "rfc4106-gcm-aesni")\" \
		cavs_driver";
CIPHER_TESTS_aesni_blkasm_iiv="AES AES_GCM"

# C ciphers with C block chaining modes
CIPHER_CALL_aesgen="KCAPI_GCM_AES=\"gcm_base(ctr(aes-generic),ghash-generic)\" \
		KCAPI_CCM_AES=\"ccm_base(ctr(aes-generic),$(cbcmac_name "aes-generic"))\" \
		KCAPI_CBC_AES=\"cbc(aes-generic)\" \
		KCAPI_ECB_AES=\"ecb(aes-generic)\" \
		KCAPI_XTS_AES=\"$(xts_name "aes-generic")\" \
		KCAPI_KW_AES=\"kw(aes-generic)\" \
		KCAPI_CMAC_AES=\"cmac(aes-generic)\" \
		cavs_driver"
CIPHER_TESTS_aesgen=$AES_TESTS

CIPHER_CALL_aesgen_iiv="KCAPI_CBC_AES=\"cbc(aes-generic)\" \
		KCAPI_ECB_AES=\"ecb(aes-generic)\" \
		KCAPI_GCM_AES=\"$(iiv_name "rfc4106(gcm_base(ctr(aes-generic),ghash-generic))")\" \
		cavs_driver";
CIPHER_TESTS_aesgen_iiv="AES AES_GCM"
CIPHER_CALL_aesgen_eiv="KCAPI_CBC_AES=\"cbc(aes-generic)\" \
		KCAPI_ECB_AES=\"ecb(aes-generic)\" \
		KCAPI_GCM_AES=\"rfc4106(gcm_base(ctr(aes-generic),ghash-generic))\" \
		cavs_driver";
CIPHER_TESTS_aesgen_eiv="AES AES_GCM"

# C cipher using constant time AES with C block chaining modes
CIPHER_CALL_aesti="KCAPI_GCM_AES=\"gcm_base(ctr(aes-fixed-time),ghash-generic)\" \
		KCAPI_CCM_AES=\"ccm_base(ctr(aes-fixed-time),$(cbcmac_name "aes-fixed-time"))\" \
		KCAPI_CBC_AES=\"cbc(aes-fixed-time)\" \
		KCAPI_ECB_AES=\"ecb(aes-fixed-time)\" \
		KCAPI_XTS_AES=\"$(xts_name "aes-fixed-time")\" \
		KCAPI_KW_AES=\"kw(aes-fixed-time)\" \
		KCAPI_CMAC_AES=\"cmac(aes-fixed-time)\" \
		cavs_driver"
CIPHER_TESTS_aesti=$AES_TESTS

CIPHER_CALL_aesti_iiv="KCAPI_CBC_AES=\"cbc(aes-fixed-time)\" \
		KCAPI_ECB_AES=\"ecb(aes-fixed-time)\" \
		KCAPI_GCM_AES=\"$(iiv_name "rfc4106(gcm_base(ctr(aes-fixed-time),ghash-generic))")\" \
		cavs_driver";
CIPHER_TESTS_aesti_iiv="AES AES_GCM"
CIPHER_CALL_aesti_eiv="KCAPI_CBC_AES=\"cbc(aes-fixed-time)\" \
		KCAPI_ECB_AES=\"ecb(aes-fixed-time)\" \
		KCAPI_GCM_AES=\"rfc4106(gcm_base(ctr(aes-fixed-time),ghash-generic))\" \
		cavs_driver";
CIPHER_TESTS_aesti_eiv="AES AES_GCM"

################### SHA TESTS #######################
SHA_TESTS="SHA HMAC DRBG800-90A RSA2"
#SHA_TESTS="SHA HMAC DRBG800-90A RSA2 KDF800-108"
CIPHER_CALL_shagen="KCAPI_SHA1=\"sha1-generic\" \
		KCAPI_SHA224=\"sha224-generic\" \
		KCAPI_SHA256=\"sha256-generic\" \
		KCAPI_SHA384=\"sha384-generic\" \
		KCAPI_SHA512=\"sha512-generic\" \
		cavs_driver"
CIPHER_TESTS_shagen=$SHA_TESTS

CIPHER_CALL_shassse3="KCAPI_SHA1=\"sha1-ssse3\" \
		KCAPI_SHA224=\"sha224-ssse3\" \
		KCAPI_SHA256=\"sha256-ssse3\" \
		KCAPI_SHA384=\"sha384-ssse3\" \
		KCAPI_SHA512=\"sha512-ssse3\" \
		cavs_driver"
CIPHER_TESTS_shassse3=$SHA_TESTS

CIPHER_CALL_shaavx="KCAPI_SHA1=\"sha1-avx\" \
		KCAPI_SHA224=\"sha224-avx\" \
		KCAPI_SHA256=\"sha256-avx\" \
		KCAPI_SHA384=\"sha384-avx\" \
		KCAPI_SHA512=\"sha512-avx\" \
		cavs_driver"
CIPHER_TESTS_shaavx=$SHA_TESTS

CIPHER_CALL_shaavx2="KCAPI_SHA1=\"sha1-avx2\" \
		KCAPI_SHA224=\"sha224-avx2\" \
		KCAPI_SHA256=\"sha256-avx2\" \
		KCAPI_SHA384=\"sha384-avx2\" \
		KCAPI_SHA512=\"sha512-avx2\" \
		cavs_driver"
CIPHER_TESTS_shaavx2=$SHA_TESTS

CIPHER_CALL_shamb="KCAPI_SHA1=\"sha1_mb\" \
		KCAPI_SHA256=\"sha256_mb\" \
		KCAPI_SHA512=\"sha512_mb\" \
		cavs_driver"
CIPHER_TESTS_sha2mb="SHA DRBG800-90A RSA2"

################### TDES TESTS #####################

CIPHER_CALL_x86tdesblkasm="KCAPI_ECB_TDES=\"ecb-des3_ede-asm\" \
		KCAPI_CBC_TDES=\"cbc-des3_ede-asm\" \
		cavs_driver"
CIPHER_TESTS_x86tdesblkasm="TDES"

CIPHER_CALL_x86tdesasm="KCAPI_ECB_TDES=\"ecb(des3_ede-asm)\" \
		KCAPI_CBC_TDES=\"cbc(des3_ede-asm)\" \
		KCAPI_CMAC_TDES=\"cmac(des3_ede-asm)\" \
		cavs_driver"
CIPHER_TESTS_x86tdesasm="TDES"

################### CATCHALL #######################
# All ciphers, except SHA, AES
CIPHER_CALL_remain="cavs_driver"
# CMAC is CMAC(TDES)
CIPHER_TESTS_remain="TDES CMAC KAS"
#CIPHER_TESTS_remain="TDES CMAC KAS KDF800-108"

################### Heavy Lifting #######################
sanity() {
	[ ! -d "$TESTDIR" ] && {
		echo "$TESTDIR does not exist"
		exit 1
	}
}

failures=0
do_test() {
	PATH=.:$PATH

	for exec in $EXEC; do
		eval CIPHER_CALL=\$CIPHER_CALL_$exec
		eval CIPHER_TESTS=\$CIPHER_TESTS_$exec

		# In case of SHA, change the directory of the vectors
		# depending on the CPU support
		if ! $(check_min_kernelver 4 4); then
			if [ x"$exec" = "xshassse3" ]; then
				if $(grep -q avx /proc/cpuinfo) ; then
					exec="shaavx"
				fi
				if $(grep -q avx2 /proc/cpuinfo) ; then
					exec="shaavx2"
				fi
			fi
		fi

		[ ! -d "$TESTDIR/$exec" ] && {
			echo "directory $exec does not exist"
			continue
		}

		for type in $CIPHER_TESTS; do
			dir="$TESTDIR/$exec/$type"
			[ ! -d "$dir" -a ! -L "$dir" ] && {
				echo "directory $dir does not exist"
				continue
			}

			for test in $(find $dir/ -name "*.req"); do
				respdir="$(dirname $(dirname $test))/resp"
				respfile=$(basename $test)
				respfile="${respfile%%.req}.rsp"
				respfile="$respdir/$respfile"

				[ ! -d "$respdir" ] && mkdir -p $respdir
				echo "Executing $test"
				eval PATH=$(dirname $0):$PATH $CIPHER_CALL $test $respfile
				ret=$?
				if [ $ret -ne 0 ]; then
					echo "Execution of $test failed: $ret"
					echo "Test invocation:"
					echo "PATH=$(dirname $0):$PATH $CIPHER_CALL $test $respfile"
					exit $ret
				fi

				# First test special expected results
				faxfile="$(basename $test)"
				faxfile="${faxfile%%.req}.rsp"
				faxfile="$(dirname $(dirname $test))/expected/$faxfile"
				# If this is not available, then test FAX files
				if [ ! -f "$faxfile" ]; then
					faxfile="$(basename $test)"
					faxfile="${faxfile%%.req}.fax"
					faxfile="$(dirname $(dirname $test))/fax/$faxfile"
				fi
				if [ -f "$faxfile" ]; then
					if [ $(basename $faxfile) = "gcmEncryptIntIV128.fax" -o \
					$(basename $faxfile) = "gcmEncryptIntIV192.fax" -o \
					$(basename $faxfile) = "gcmEncryptIntIV256.fax" -o \
					$(basename $faxfile) = "SigGen15_186-3.fax" ]; then
						echo "Check $respfile with CAVS tool"
						continue;
					fi
					if ! $(diff -wB <(grep -vE '^\s*#' $respfile) <(grep -vE '^\s*#' $faxfile) > /dev/null); then
						echo "Test result for $test inconsistent with expected result"
						let failures=($failures+1)
					fi
				fi
			done

                done
	done
}

for i in sha1-ssse3 sha256-ssse3 sha512-ssse3 sha1_mb sha512_mb sha256_mb des3_ede-x86_64 aes_ti; do
	modprobe $i > /dev/null 2>&1
done

sanity
do_test

if [ $failures -gt 0 ]; then
	echo "Total number of failures: $failures"
	exit $failures
else
	echo "Clean test run"
	exit 0
fi

