#!/bin/bash -e
# Copyright (C) 2024 Simo Sorce <simo@redhat.com>
# SPDX-License-Identifier: Apache-2.0

source "${TESTSSRCDIR}/helpers.sh"

# Run tests only if OpenSSL does have SKEY support
if [[ "${SUPPORT_SKEY}" = "0" ]]; then
    exit 77
fi

ORIG_OPENSSL_CONF=${OPENSSL_CONF}
# We need to force the provider and configure early loading otherwise no
# ciphers are loaded, and operations will fail
sed -e "s/#MORECONF/alg_section = algorithm_sec\n\n[algorithm_sec]\ndefault_properties = ?provider=pkcs11/" \
    -e "s/#pkcs11-module-load-behavior/pkcs11-module-load-behavior = early/" \
    "${OPENSSL_CONF}" > "${OPENSSL_CONF}.ciphers"
OPENSSL_CONF=${OPENSSL_CONF}.ciphers

RUN_SKEYMGMT_TEST="0"
if [[ "${SUPPORT_SYMMETRIC}" = "1" ]]; then
    # shellcheck disable=SC2089 # Quoted quotes is intentional
    PROP='-propquery "provider=pkcs11"'
    RUN_SKEYMGMT_TEST="1"
else
    if [[ "${OPENSSL_WITH_27483_FIX}" = "1" ]]; then
        RUN_SKEYMGMT_TEST="1"
    fi
fi

iv_needed() {
    # using tr instead of ${var,,} because of macos
    mode=$(echo "$1" |tr '[:upper:]' '[:lower:]')
    res=1
    if echo "$mode" |grep -qE "^(cbc|cfb|ofb|ctr)"; then
        res=0
    fi
    return $res
}

#
# Function to test a block cipher mode
test_block_mode() {
    local mode_name="$1" # mode, e.g., "CBC"
    local cipher="$2" # ossl cipher without mode, e.g., "aes-128"
    local input_len="$3" # plaintext length to test
    local expected_outlen="$4" # expected output length
    local key_mng="$5" # key management, "legacy" or "skey"

    # generate plaintext of length $input_len
    printf 'Plaintext%.0s' {1..100} |head -c "$input_len" >"${TMPPDIR}"/plaintext

    # using tr instead of ${var,,} because of macos
    mode_lower=$(echo "$mode_name" |tr '[:upper:]' '[:lower:]')
    ossl_cipher="-$cipher-$mode_lower"
    if [[ -z $ossl_cipher ]]; then
       echo "Unknown ossl cipher for mode $mode_name"
       exit 1
    fi
    ossl_args=("$ossl_cipher")

    if iv_needed "$mode_name"; then
        ossl_args+=("-iv 100f0e0d0c0b0a090807060504030201")
    fi

    KEY="0102030405060708090a0b0c0d0e0f10"
    if [[ $key_mng = "legacy" ]]; then
        ossl_args+=("-K $KEY")
    elif [[ $key_mng = "skey" ]]; then
        ossl_args+=("-skeymgmt AES")
        ossl_args+=("-skeyopt hexraw-bytes:$KEY")
    else
        exit 1
    fi

    title PARA "Test Symmetric Encryption AES ${mode_name} mode ($key_mng init)"
    ossl "enc -e ${ossl_args[*]}
             -in ${TMPPDIR}/plaintext
             -out ${TMPPDIR}/output.enc" "${PROP}"

    ossl "enc -d ${ossl_args[*]}
             -in ${TMPPDIR}/output.enc
             -out ${TMPPDIR}/output.dec" "${PROP}"

    diff "${TMPPDIR}/plaintext" "${TMPPDIR}/output.dec"

    # wc works on both linux and macos
    outlen=$(wc -c <"${TMPPDIR}"/output.enc)
    if ! (( outlen == expected_outlen )); then
        hexdump -C "${TMPPDIR}"/output.enc
        exit 1
    fi

    rm -f "${TMPPDIR}/plaintext"
}

for cipher in "aes-128" "aes-256"; do
    # modes with padding
    mode="CBC"
    if [[ $SUPPORT_BLOCK_MODES =~ $mode ]]; then
        for len in 1 15 16 17 31 32 33 63 64 65 127 128 129 254 255 256; do
            out_len=$(( (len / 16 + 1)*16 ))
            test_block_mode "$mode" "$cipher" "$len" "$out_len" "legacy"
            if [[ "${RUN_SKEYMGMT_TEST}" = "1" ]]; then
                test_block_mode "$mode" "$cipher" "$len" "$out_len" "skey"
            fi
        done
    fi

    # stream modes
    for mode in "OFB" "CFB" "CFB1" "CFB8" "CTR"; do
        if [[ $SUPPORT_BLOCK_MODES =~ $mode ]]; then
            for len in 1 15 16 17 31 32 33 63 64 65 127 128 129 254 255 256; do
                out_len=$len
                test_block_mode "$mode" "$cipher" "$len" "$out_len" "legacy"
                if [[ "${RUN_SKEYMGMT_TEST}" = "1" ]]; then
                    test_block_mode "$mode" "$cipher" "$len" "$out_len" "skey"
                fi
            done
        fi
    done

    # modes withOUT padding
    mode="ECB"
    if [[ $SUPPORT_BLOCK_MODES =~ $mode ]]; then
        for len in 16 32 64 128 256; do
            out_len=$len
            test_block_mode "$mode" "$cipher" "$len" "$out_len" "legacy"
            if [[ "${RUN_SKEYMGMT_TEST}" = "1" ]]; then
                test_block_mode "$mode" "$cipher" "$len" "$out_len" "skey"
            fi
        done
    fi
done

title PARA "Test Symmetric Key Generation"
if [[ "${SUPPORT_SYMMETRIC}" = "1" ]]; then
    ossl 'skeyutl -genkey -skeymgmt AES -cipher aes128
                  -skeyopt "pkcs11_uri:pkcs11:id=%55%33\;object=SECRET-SKEY"
                  -skeyopt "pkcs11_ephemeral:0"'
    P11DEFLOGIN=("--login" "--pin=${PINVALUE}")
    ptool -O | grep SECRET-SKEY
else
    FAIL=0
    ossl 'skeyutl -genkey -skeymgmt AES -cipher aes128
                  -skeyopt "pkcs11_uri:pkcs11:id=%55%33\;object=SECRET-SKEY"
                  -skeyopt "pkcs11_ephemeral:0"' || FAIL=1
    if [ $FAIL -eq 0 ]; then
        echo "Skeyutil genkey operation should have failed"
        exit 1
    fi
fi

title PARA "Test Skey HKDF"
FAIL=0
output=$($CHECKER "${TESTBLDDIR}"/tskey-kdf 2>&1) || FAIL=1
if [ $FAIL -ne 0 ]; then
    echo
    echo "Original command output:"
    echo "$output"
    echo
    exit 1
fi

OPENSSL_CONF=${ORIG_OPENSSL_CONF}

exit 0
