blob: e693ac31ba7af114678c11c65290f709a2de7572 [file] [log] [blame]
#!/bin/sh
# This uses the public/private keys on a PIV device, like a CAC or Yubikey.
# It requires a user-entered PIN.
# It uses OpenSSL with PKCS11 enabled via OpenSC.
# This stores the cluster encryption key encrypted with the PIV public
# key in $DIR. This is technically a three-level encryption
# architecture, with the third level requiring the PIV and PIN.
# Do not create any fie with extension "wkey" in $DIR; these are
# reserved for wrapped data key files.
[ "$#" -lt 2 ] && echo "cluster_key_command usage: $0 \"%d\" %R [\"%p\"]" 1>&2 && exit 1
# Supports environment variable PROMPT
DIR="$1"
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
FD="$2"
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
[ "$3" ] && PROMPT="$3"
# PIV slot 3 is the "Key Management" slot, so we use '0:3'
PIV_SLOT='0:3'
# File containing the cluster key encrypted with the PIV_SLOT's public key
KEY_FILE="$DIR/pivpass.key"
# ----------------------------------------------------------------------
[ ! "$PROMPT" ] && PROMPT='Enter PIV PIN: '
stty -echo <&"$FD"
# Create a cluster key encrypted with the PIV_SLOT's public key?
if [ ! -e "$KEY_FILE" ]
then echo 1>&"$FD"
echo -n "$PROMPT" 1>&"$FD"
# The 'postgres' operating system user must have permission to
# access the PIV device.
openssl rand -hex 32 |
# 'engine "pkcs11" set.' message confuses prompting
if ! openssl rsautl -engine pkcs11 -keyform engine -encrypt \
-inkey "$PIV_SLOT" -passin fd:"$FD" -out "$KEY_FILE" 2>&1
then stty echo <&"$FD"
echo 'cluster key generation failed' 1>&2
exit 1
fi | grep -v 'engine "pkcs11" set\.'
echo 1>&"$FD"
# Warn the user to save the cluster key in a safe place
cat 1>&"$FD" <<END
WARNING: The PIV can be locked and require a reset if too many PIN
attempts fail. It is recommended to run this command manually and save
the cluster key in a secure location for possible recovery.
END
fi
echo 1>&"$FD"
echo -n "$PROMPT" 1>&"$FD"
# Decrypt the cluster key encrypted with the PIV_SLOT's public key
if ! openssl rsautl -engine pkcs11 -keyform engine -decrypt \
-inkey "$PIV_SLOT" -passin fd:"$FD" -in "$KEY_FILE" 2>&1
then stty echo <&"$FD"
echo 'cluster key retrieval failed' 1>&2
exit 1
fi | grep -v 'engine "pkcs11" set\.'
echo 1>&"$FD"
stty echo <&"$FD"
exit 0