#! /bin/bash
# zdkimgenkey --freely rewritten based on opendkim-genkey
# generate a key and/or TXT record for DKIM service
# Copyright (c) 2021, Alessandro Vesely
# 
# This file is part of zdkimfilter
# 
# zdkimfilter 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.
# 
# zdkimfilter 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 version 3
# along with zdkimfilter.  If not, see <http://www.gnu.org/licenses/>.
# 
# Additional permission under GNU GPLv3 section 7:
# 
# If you modify zdkimfilter, or any covered part of it, by linking or combining
# it with OpenSSL, OpenDKIM, Sendmail, or any software developed by The Trusted
# Domain Project or Sendmail Inc., containing parts covered by the applicable
# licence, the licensor of zdkimfilter grants you additional permission to convey
# the resulting work.
# 

# Default values
alg="rsa"
bits="2048"
domain="example.com"
outdir="/etc/courier/filters/keys"
selector="default"
progname=`basename $0`
use_certtool=

usage="
Usage: $progname [options]
     -a              use alternative tool (certtool vs openssl)
     -b bits         key size (default = $bits)
     -d domain       signing domain (default = \"example.com\")
     -e              generate ed25519, not RSA key
     -h              this help
     -D dir          output directory (default = \"$outdir\")
     -s name         selector name (default = \"default\")

The output of this script consists of two files based on the
selector name:

    <selector>.private  PEM-formatted private key, for use by
                         zdkimfilter when signing messages.

    <selector>.txt      A DNS TXT record suitable for insertion into or
                        inclusion by a DNS zone file in order to
                        publish the public key for verifiers.
                        PLEASE AWAIT FOR PUBLIC KEY PUBLICATION BEFORE
                        LINKING A DOMAIN NAME TO THE NEW KEY.
"

# Argument processing
while [ $# -gt 0 ]; do
	case $1 in
	-a)
		if (($use_certtool)); then
			use_certtool=0
		else
			use_certtool=1
		fi
		;;
	-b)
		if [ $# -eq 1 ]; then
			echo $progname: -b requires a value
			exit 1
		fi

		shift
		bits=$1
		;;

	-d)
		if [ $# -eq 1 ]; then
			echo $progname: -d requires a value
			exit 1
		fi

		shift
		domain=$1
		;;

	-D)
		if [ $# -eq 1 ]; then
			echo $progname: -D requires a value
			exit 1
		fi

		shift
		outdir=$1
		;;

	-e) alg="ed25519";;

	--help | -h)
		printf "$usage"
		exit 0
		;;

	-s)
		if [ $# -eq 1 ]; then
			echo $progname: -s requires a value
			exit 1
		fi

		shift
		selector=$1
		;;

	*) echo $progname: unknown flag $1
		exit 1
		;;
	esac

	shift
done

# do this securely and in the right place
mkdir -p $outdir
if [ ! -w "$outdir" ]; then
	echo cannot write to $outdir
	exit 1
fi
if ! cd $outdir; then
	echo cannot cd to $outdir
	exit 1
fi
if [ -f "$selector.private" ]; then
	echo "$selector.private already exists"
	exit 1
fi
if [ -f "$selector.txt" ]; then
	echo "$selector.txt already exists"
	exit 1
fi
umask 077

# DNS record with open parenthesis an open quote
dnsrec="$selector._domainkey.${domain%.}. IN TXT ( \"v=DKIM1; k=$alg; "

if [ "$alg" = "ed25519" ]; then
	bits=""
fi
if (($use_certtool)); then
	if [ -n "$bits" ]; then
		bitoption="--bits=$bits"
	fi
	certtool --generate-privkey --key-type=$alg $bitoption --outfile=$selector.private
	getpubkey="certtool --load-privkey $selector.private --pubkey-info"
else
	if [ -n "$bits" ]; then
		bitoption="-pkeyopt rsa_keygen_bits:$bits"
	fi
	openssl genpkey -algorithm $alg $bitoption -out $selector.private
	getpubkey="openssl pkey -in $selector.private -pubout"
fi

# Use sed to join lines without inserting spaces.
keydata="p=$($getpubkey| sed -n '/^-----BEGIN/,/^-----END/{/^[^-]/H};${g;s/\n//g;p}')"

# Close quote on every line, close parenthesis on last line
echo "$dnsrec\"" > $selector.txt

if [ "$alg" = "ed25519" ]; then
	# remove the prefix
	echo "	\"$keydata\" )" | sed 's/MCowBQYDK2VwAyEA//' >> $selector.txt
else
	declare -i off=0
	while ((off + 54 < ${#keydata})); do
		echo "	\"${keydata: $off: 50}\"" >> $selector.txt
		((off+=50))
	done
	echo "	\"${keydata: $off}\" )" >> $selector.txt
fi
