Skip to content

Instantly share code, notes, and snippets.

@leophys
Created November 10, 2021 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leophys/350ee2b24170035bfd54acd9e4bb7f06 to your computer and use it in GitHub Desktop.
Save leophys/350ee2b24170035bfd54acd9e4bb7f06 to your computer and use it in GitHub Desktop.
Use your tpm2 module to generate an ssh key (archlinux only)
#!/usr/bin/env bash
#
# DISCLAIMER: I am not sure this script won't nuke the content of your tpm module, so please,
# accept this risk before continuing.
#
# This script condensates the receipt found at https://incenp.org/notes/2020/tpm-based-ssh-key.html
# for an archlinux system. Running it will eventually install the dependencies, initialize the
# tpm2_pkcs11 store and generate a token and a key. From the key, an ssh public key will be generated.
# You can customize the some setting with env variables: KEY_PATH overrides the default path where the
# key is stored (~/.ssh/tpm2.key); with KEY_ALGO you can override the algorithm used to generate the key
# (issue tpm2_ptool addkey --help to list the supported algorithms).
#
# When the key has been generated, you can use it specifying
#
# $ ssh -I /usr/local/lib/pkcs11/libtpm2_pkcs11.so myserver.example
#
# or putting this in the ssh config
#
# Host myserver.example
# PKCS11Provider /usr/lib/pkcs11/libtpm2_pkcs11.so
# IdentityAgent none
set -o pipefail
if [ "z${DEBUG}" != "z" ]
then
set -x
fi
_PINENTRY=${PINENTRY:-"pinentry-gnome3"}
_KEY_PATH=${KEY_PATH:-"${HOME}/.ssh/tpm2.key"}
export TPM2_PKCS11_TCTI=tabrmd:
function get_pin() {
PROMPT1=$(cat <<EOP
SETPROMPT Please enter the ${1}:
GETPIN
EOP
)
PROMPT2=$(cat <<EOP
SETPROMPT Please enter the ${1} again to confirm:
GETPIN
EOP
)
PIN1=$(echo "${PROMPT1}" | \
${_PINENTRY} | \
sed -nr '0,/^D (.+)/s//\1/p')
PIN2=$(echo "${PROMPT2}" | \
${_PINENTRY} | \
sed -nr '0,/^D (.+)/s//\1/p')
if [ "${PIN1}" != "${PIN2}" ]; then
return 1
else
PIN=${PIN1}
fi
return 0
}
function get_sopin() {
while ! get_pin "sopin"
do
echo "Insert sopin"
done
SOPIN=$PIN
}
function get_userpin() {
while ! get_pin "userpin"
do
echo "Insert userpin"
done
USERPIN=$PIN
}
function check_dep_arch() {
IS_INSTALLED=$(pacman -Q ${1} 2>/dev/null|wc -l)
if [ ${IS_INSTALLED} -eq 0 ]
then
return 1
fi
return 0
}
function check_deps_arch() {
DEPS=$(cat <<EOD
openssh
tpm2-tss
tpm2-abrmd
tpm2-pkcs11
tpm2-tools
yq
EOD
)
TO_BE_INSTALLED=$(echo "${DEPS}" |\
while IFS=$'\n' read dep
do
if ! check_dep_arch "${dep}"
then
echo -n " ${dep}"
fi
done)
if [ "z${TO_BE_INSTALLED}" != "z" ]
then
ask_to_install "${TO_BE_INSTALLED}"
fi
}
function ask_to_install() {
echo "These packages are missing:${1}"
read -p "Do you want me to install them? [y/n]" -n 1 -r
echo
if [ "${REPLY}" != "y" ] && [ "${REPLY}" != "Y" ]
then
echo "Manually install the dependencies and re-run this script"
exit 1
fi
sudo pacman -Sy ${1}
}
function maybe_init_ptool() {
if ! [ -d ${HOME}/.tpm2_pkcs11 ]
then
echo "Initing. This could take a while. Do not terminate."
mkdir ${HOME}/.tpm2_pkcs11
tpm2_ptool init
fi
}
function check_token() {
ID=$(tpm2_ptool listprimaries|yq '.[] | .id')
ID_LEN=$(echo "${ID}"|wc -l)
if [ ${ID_LEN} -eq 0 ]
then
echo "No primary detected. Exiting"
exit -1
elif ! [ ${ID_LEN} -eq 1 ]
then
select id in "${ID}"
do
ID="${id}"
break
done
fi
TOKENS=$(tpm2_ptool listtokens --pid=${ID}|yq '.[] | .id')
TOKENS_LEN=$(echo -n "${TOKENS}"|wc -l)
if [ ${TOKENS_LEN} -eq 0 ]
then
generate_token ${ID}
else
select token_id in "${TOKENS}"
do
TOKEN_ID=${token_id}
TOKEN_LABEL=$(tpm2_ptool listtokens --pid=${ID}|yq ".[] | select(.id == ${token_id}) | .label")
break
done
fi
}
function generate_token() {
tpm2_ptool addtoken \
--pid=${1} \
--label=${TOKEN_LABEL:-"ssh_token"} \
--sopin=${SOPIN} \
--userpin=${USERPIN}
}
function generate_key() {
export TSS2_LOG=fapi+NONE
tpm2_ptool addkey \
--label=${TOKEN_LABEL:-"ssh_token"} \
--userpin=${USERPIN} \
--algorithm=${KEY_ALGO:-"ecc256"} && \
ssh-keygen -D /usr/lib/pkcs11/libtpm2_pkcs11.so > ${_KEY_PATH}
}
function check_key() {
if [ -f ${_KEY_PATH} ]
then
echo "The key yet exists at ${_KEY_PATH}. Aborting."
exit 2
fi
}
check_key
check_deps_arch
get_userpin
get_sopin
maybe_init_ptool
check_token
generate_key
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment