#!/bin/bash
# SPDX-License-Identifier: GPL-3.0-or-later WITH cryptsetup-OpenSSL-exception


# This file is part of sscg.
#
# sscg is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# sscg 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.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with sscg.  If not, see <http://www.gnu.org/licenses/>.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the
# OpenSSL library under certain conditions as described in each
# individual source file, and distribute linked combinations
# including the two.
# You must obey the GNU General Public License in all respects
# for all of the code used other than OpenSSL.  If you modify
# file(s) with this exception, you may extend this exception to your
# version of the file(s), but you are not obligated to do so.  If you
# do not wish to do so, delete this exception statement from your
# version.  If you delete this exception statement from all source
# files in the program, then also delete it here.
#
# Copyright 2022-2025 by Stephen Gallagher <sgallagh@redhat.com>


# ARG_OPTIONAL_SINGLE([hash-alg],[],[Certificate hashing algorithm],[sha256])
# ARG_OPTIONAL_SINGLE([cipher-alg],[],[Key password cipher algorithm],[aes-256-cbc])
# ARG_OPTIONAL_SINGLE([key-strength],[],[Certificate key strength],[2048])
# ARG_HELP([Test for validity of created certificates])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.9.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
# Generated online by https://argbash.io/generate


# # When called, the process ends.
# Args:
# 	$1: The exit message (print to stderr)
# 	$2: The exit code (default is 1)
# if env var _PRINT_HELP is set to 'yes', the usage is print to stderr (prior to $1)
# Example:
# 	test -f "$_arg_infile" || _PRINT_HELP=yes die "Can't continue, have to supply file as an argument, got '$_arg_infile'" 4
die()
{
	local _ret="${2:-1}"
	test "${_PRINT_HELP:-no}" = yes && print_help >&2
	echo "$1" >&2
	exit "${_ret}"
}


# Function that evaluates whether a value passed to it begins by a character
# that is a short option of an argument the script knows about.
# This is required in order to support getopts-like short options grouping.
begins_with_short_option()
{
	local first_option all_short_options='h'
	first_option="${1:0:1}"
	test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}

# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_hash_alg="sha256"
_arg_cipher_alg="aes-256-cbc"
_arg_key_strength="2048"


# Function that prints general usage of the script.
# This is useful if users asks for it, or if there is an argument parsing error (unexpected / spurious arguments)
# and it makes sense to remind the user how the script is supposed to be called.
print_help()
{
	printf '%s\n' "Test for validity of created certificates"
	printf 'Usage: %s [--hash-alg <arg>] [--cipher-alg <arg>] [--key-strength <arg>] [-h|--help]\n' "$0"
	printf '\t%s\n' "--hash-alg: Certificate hashing algorithm (default: 'sha256')"
	printf '\t%s\n' "--cipher-alg: Key password cipher algorithm (default: 'aes-256-cbc')"
	printf '\t%s\n' "--key-strength: Certificate key strength (default: '2048')"
	printf '\t%s\n' "-h, --help: Prints help"
}


# The parsing of the command-line
parse_commandline()
{
	while test $# -gt 0
	do
		_key="$1"
		case "$_key" in
			# We support whitespace as a delimiter between option argument and its value.
			# Therefore, we expect the --hash-alg value, so we watch for --hash-alg.
			# Since we know that we got the long option,
			# we just reach out for the next argument to get the value.
			--hash-alg)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_hash_alg="$2"
				shift
				;;
			# We support the = as a delimiter between option argument and its value.
			# Therefore, we expect --hash-alg=value, so we watch for --hash-alg=*
			# For whatever we get, we strip '--hash-alg=' using the ${var##--hash-alg=} notation
			# to get the argument value
			--hash-alg=*)
				_arg_hash_alg="${_key##--hash-alg=}"
				;;
			# See the comment of option '--hash-alg' to see what's going on here - principle is the same.
			--cipher-alg)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_cipher_alg="$2"
				shift
				;;
			# See the comment of option '--hash-alg=' to see what's going on here - principle is the same.
			--cipher-alg=*)
				_arg_cipher_alg="${_key##--cipher-alg=}"
				;;
			# See the comment of option '--hash-alg' to see what's going on here - principle is the same.
			--key-strength)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_key_strength="$2"
				shift
				;;
			# See the comment of option '--hash-alg=' to see what's going on here - principle is the same.
			--key-strength=*)
				_arg_key_strength="${_key##--key-strength=}"
				;;
			# The help argurment doesn't accept a value,
			# we expect the --help or -h, so we watch for them.
			-h|--help)
				print_help
				exit 0
				;;
			# We support getopts-style short arguments clustering,
			# so as -h doesn't accept value, other short options may be appended to it, so we watch for -h*.
			# After stripping the leading -h from the argument, we have to make sure
			# that the first character that follows coresponds to a short option.
			-h*)
				print_help
				exit 0
				;;
			*)
				_PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1
				;;
		esac
		shift
	done
}

# Now call all the functions defined above that are needed to get the job done
parse_commandline "$@"

# OTHER STUFF GENERATED BY Argbash

### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash


TMPDIR=$(mktemp --directory --tmpdir sscg_XXXXXX)

function common_finalize {
    exitcode=$?
    rm -Rf "$TMPDIR"
    return $exitcode
}

trap common_finalize EXIT

# Fail the test if any command returns non-zero
set -ex

pushd "$TMPDIR"

"${MESON_BUILD_ROOT}"/sscg --hash-alg "$_arg_hash_alg" \
                         --cipher-alg "$_arg_cipher_alg" \
						 --key-strength "$_arg_key_strength" \
						 --cert-key-password mypassword \
						 --dhparams-file "dhparams.pem"

# Verify that the expected files were created
test -e dhparams.pem
test -e ca.crt
test -e service.pem
test -e service-key.pem

# Verify that they have the correct file format
openssl dhparam -noout -in dhparams.pem
openssl x509    -noout -in ca.crt
openssl x509    -noout -in service.pem

grep "ENCRYPTED PRIVATE KEY" service-key.pem
openssl pkey    -noout -in service-key.pem -passin pass:mypassword

# Verify the key strength
key_strength=$(openssl pkey -text -noout -in service-key.pem -passin pass:mypassword|head -n1 |sed 's/.*Private-Key: (\([0-9]\{1,\}\).*/\1/')
test "$key_strength" -eq "$_arg_key_strength"

# Validate the certificates
openssl verify -x509_strict -CAfile ca.crt service.pem

popd # $TMPDIR



# ] <-- needed because of Argbash
