#!/bin/sh








#********************************************************************************
#*** Initialisation                                                           ***
#********************************************************************************
#parse the options to cryptsetup required for this key-script
target="${CRYPTTAB_NAME}"

#read all the options to the Cryptsetup OpenPGP Scripts, specified via the third field of the respective entry in “/etc/crypttab”
cryptsetup_openpgp_scripts_options="${CRYPTTAB_KEY}"
#parse the options to the Cryptsetup OpenPGP Scripts required for this key-script
pathname="$( printf "%s" "${cryptsetup_openpgp_scripts_options}"  |  sed -n 's/^\(.*:\)*pathname=\([^:]*\)\(:.*\)*$/\2/p' )"
device="$( printf "%s" "${cryptsetup_openpgp_scripts_options}"  |  sed -n 's/^\(.*:\)*device=\([^:]*\)\(:.*\)*$/\2/p' )"
key_file_in_initramfs_images="$( printf "%s" "${cryptsetup_openpgp_scripts_options}"  |  sed -n 's/^\(.*:\)*key_file_in_initramfs_images\(:.*\)*$/yes/p' )"
timeout="$( printf "%s" "${cryptsetup_openpgp_scripts_options}"  |  sed -n 's/^\(.*:\)*timeout=\([^:]*\)\(:.*\)*$/\2/p' )"


#check whether the “pathname”-option to the Cryptsetup OpenPGP Scripts is set
if [ -z "${pathname}" ]; then
	printf "Error: The third field of the “${target}”-entry in “/etc/crypttab” does not set the “pathname”-option to the Cryptsetup OpenPGP Scripts.\n" >&2
	exit 2
fi

#check whether both, the “device”- and the “key_file_in_initramfs_images”-option to the Cryptsetup OpenPGP Scripts are set and if so print a warning
if [ -n "${device}"  -a  "${key_file_in_initramfs_images}" = yes ]; then
	printf "Warning: The third field of the “${target}”-entry in “/etc/crypttab” sets both, the “device”- and the “key_file_in_initramfs_images”-option to the Cryptsetup OpenPGP Scripts. The latter is ignored.\n" >&2
fi




#Note: “passdev” is part of the “cryptsetup”-package. This is also the package of the Cryptsetup OpenPGP Scripts, thus it is not necessary to check whether it is installed. It is also included in initramfs images via cryptopenpgp if required.

#Note: “base64” is part of the “coreutils”-package. This is an essential package, thus it is not necessary to check whether it is installed. It is also included in initramfs images via cryptopenpgp.

#Note: “askpass” is part of the “cryptsetup”-package. This is also the package of the Cryptsetup OpenPGP Scripts, thus it is not necessary to check whether it is installed. It is also included in initramfs images via the “cryptroot”-hook-script.




#TODO: Support other OpenPGP-implementations than “GnuPG”.
#********************************************************************************
#*** Check Whether An OpenPGP-Implementation Is Available                     ***
#********************************************************************************
#Note: The dpkg database cannot be used for these checks, as it is not available “within” an initramfs image (during booting).

#check for “GnuPG”
if [ ! -x /usr/bin/gpg ]; then
	printf "Error: No known OpenPGP-implementation is available.\n" >&2
	exit 1
fi




#********************************************************************************
#*** Read The Key                                                             ***
#********************************************************************************
#Note: Using a function, error-marks and tricky redirections here is necessary for proper error handling. Particularly, piping the command that reads the key directly to “base64” would prevent the evaluation of the exit status of the former.


read_key()
{
	if [ -n "${device}" ]; then
		#check whether the device, specified via the “device”-option to the Cryptsetup OpenPGP Scripts in the third field of the respective entry in “/etc/crypttab”, already exists and if not print an information on the waiting
		if [ ! -e "${device}" ]; then
			printf "Waiting for the device “${device}” to appear…\n" >&2
		fi
		
		#use “passdev” to read the key file
		/lib/cryptsetup/scripts/passdev "${device}":"${pathname}":"${timeout:-'-1'}" 2> /dev/null
		if [ $? -ne 0 ]; then #error handling
			printf "\nERROR\n" >&3 #mark that an error occurred
			
			printf "Error: Could not read the key file “${pathname}” on the device “${device}”, specified by the “pathname”- and the “device”-option to the Cryptsetup OpenPGP Scripts in the third field of the “${target}”-entry in “/etc/crypttab”.\n" >&2
			exit 1
		fi
	else
		#check whether the “pathname”-option to the Cryptsetup OpenPGP Scripts specifies an existing, readable and regular file
		if [ ! -r "${pathname}"  -o  ! -f "${pathname}" ]; then
			printf "\nERROR\n" >&3 #mark that an error occurred
			
			printf "Error: The key file “${pathname}”, specified by the “pathname”-option to the Cryptsetup OpenPGP Scripts in the third field of the “${target}”-entry in “/etc/crypttab”, does not exist, is not readable or is not a regular file.\n" >&2
			exit 1
		fi
	
		#read the key file directly
		cat "${pathname}" 2> /dev/null
		if [ $? -ne 0 ]; then #error handling
			printf "\nERROR\n" >&3 #mark that an error occurred
			
			printf "Error: Could not read the key file “${pathname}”, specified by the “pathname”-option to the Cryptsetup OpenPGP Scripts in the third field of the “${target}”-entry in “/etc/crypttab”.\n" >&2
			exit 1
		fi
	fi
}


#read the key and encode it to the “Base64 Data Encoding” (see RFC 4648)
#Note: Encoding the key to the “Base64 Data Encoding” is necessary, as it might contain any octet or character (for example U+0000 “NULL”).
#Note: The temporary file descriptor “3” is used to let the error-marks bypass the encoding to the “Base64 Data Encoding”.
exec 3>&2 #open temporary file descriptor
encrypted_key="$( { read_key  |  base64 -w 0 ; }   3>&1 )"
exec 3>&- #close temporary file descriptor

#check whether an error-mark is set and if so exit
if [ "$( printf "%s" "${encrypted_key}"  |  wc -l )"   -gt   0 ]; then
	exit 1
fi




#********************************************************************************
#*** Read The Passphrase                                                      ***
#********************************************************************************
passphrase="$( /lib/cryptsetup/askpass "Enter passphrase: " )"
if [ $? -ne 0 ]; then #error handling
	printf "Error: Could not read the passphrase.\n" >&2
	exit 1
fi




#TODO: Support other OpenPGP-implementations than “GnuPG”.
#********************************************************************************
#*** Decrypt The Key                                                          ***
#********************************************************************************
#Note: Using a function, error-marks and tricky redirections here is necessary for proper error handling. Particularly, piping the command that decrypts the key directly to “base64” would prevent the evaluation of the exit status of the former.


decrypt_key()
{
	#decrypt the key using “GnuPG”
	{ printf "%s\n" "${passphrase}";   printf "%s" "${encrypted_key}"  |  base64 -d ; }    |    gpg --quiet --no-verbose --no-greeting --batch --no-options --no-random-seed-file --no-default-keyring --keyring /dev/null --secret-keyring /dev/null --trustdb-name /dev/null --passphrase-fd 0 --decrypt
	if [ $? -ne 0 ]; then #error handling
		printf "\nERROR\n" >&3 #mark that an error occurred
		
		exit 1
	fi
}


#decrypt the key and encode it to the “Base64 Data Encoding” (see RFC 4648)
#Note: Encoding the key to the “Base64 Data Encoding” is necessary, as it might contain any octet or character (for example U+0000 “NULL”).
#Note: The temporary file descriptor “3” is used to let the error-marks bypass the encoding to the “Base64 Data Encoding”.
exec 3>&2 #open temporary file descriptor
unencrypted_key="$( { decrypt_key  |  base64 -w 0 ; }   3>&1 )"
exec 3>&- #close temporary file descriptor

#check whether an error-mark is set and if so exit
if [ "$( printf "%s" "${unencrypted_key}"  |  wc -l )"   -gt   0 ]; then
	printf "Error: Could not decrypt the key.\n" >&2
	exit 1
fi




#********************************************************************************
#*** Print The Key                                                            ***
#********************************************************************************
#Note: The key must only be written to the standard output stream if was successfully decrypted.
printf "%s" "${unencrypted_key}"  |  base64 -d




exit 0
















#Copyright © 2008–2009, Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>.
#All rights reserved.
#
#
#This program 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.
#
#This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
