2023-02-08 21:01:48 +00:00
#!/bin/bash
2019-08-15 18:36:05 +00:00
# Automated setup of TPM, GPG keys, and disk
set -o pipefail
2023-03-28 19:51:45 +00:00
## External files sourced
2023-02-18 17:58:43 +00:00
. /etc/functions
2023-03-28 19:51:45 +00:00
. /etc/luks-functions
. /tmp/config
TRACE "Under /bin/oem-factory-reset"
2019-08-15 18:36:05 +00:00
# use TERM to exit on error
trap "exit 1" TERM
export TOP_PID=$$
## Static local variables
CLEAR="--clear"
CONTINUE="--yes-button Continue"
CANCEL="--no-button Cancel"
2022-11-09 16:51:27 +00:00
HEIGHT="0"
WIDTH="80"
2019-08-15 18:36:05 +00:00
USER_PIN_DEF=123456
ADMIN_PIN_DEF=12345678
TPM_PASS_DEF=12345678
2022-03-10 14:55:08 +00:00
USER_PIN=""
ADMIN_PIN=""
TPM_PASS=""
2022-06-02 17:52:51 +00:00
#Circumvent Librem Key/Nitrokey HOTP firmware bug https://github.com/osresearch/heads/issues/1167
MAX_HOTP_GPG_PIN_LENGTH=25
2022-03-10 14:55:08 +00:00
# What are the Security components affected by custom passwords
CUSTOM_PASS_AFFECTED_COMPONENTS=""
2023-10-25 21:01:01 +00:00
# Default GPG Algorithm is RSA
GPG_ALGO="RSA"
2023-10-18 17:15:48 +00:00
# Default RSA key length
2023-10-26 20:50:10 +00:00
RSA_KEY_LENGTH=3072
2020-11-24 11:48:41 +00:00
2020-01-02 16:29:11 +00:00
GPG_USER_NAME="OEM Key"
2023-10-18 17:15:48 +00:00
GPG_KEY_NAME=$(date +%Y%m%d%H%M%S)
2020-01-02 16:29:11 +00:00
GPG_USER_MAIL="oem-${GPG_KEY_NAME}@example.com"
GPG_USER_COMMENT="OEM-generated key"
2020-10-23 23:38:30 +00:00
SKIP_BOOT="n"
2020-07-07 08:32:22 +00:00
2019-08-15 18:36:05 +00:00
## functions
die() {
2023-10-18 17:15:48 +00:00
local msg=$1
if [ -n "$msg" ]; then
echo -e "\n$msg"
fi
kill -s TERM $TOP_PID
exit 1
2019-08-15 18:36:05 +00:00
}
2023-10-18 17:15:48 +00:00
whiptail_error() {
2019-08-15 18:36:05 +00:00
local msg=$1
if [ "$msg" = "" ]; then
die "whiptail error: An error msg is required"
fi
2021-11-23 20:37:09 +00:00
whiptail $BG_COLOR_ERROR --msgbox "${msg}\n\n" $HEIGHT $WIDTH $BG_COLOR_ERROR --title "Error"
2019-08-15 18:36:05 +00:00
}
2023-10-18 17:15:48 +00:00
whiptail_error_die() {
2019-08-15 18:36:05 +00:00
whiptail_error "$@"
die
}
2023-10-25 21:01:01 +00:00
#Generate a gpg master key: no expiration date, RSA 4096 bits
2023-10-18 17:15:48 +00:00
#This key will be used to sign 3 subkeys: encryption, authentication and signing
2023-10-26 20:50:10 +00:00
#The master key and subkeys will be copied to backup, and the subkeys moved from memory keyring to the smartcard
2023-10-18 17:15:48 +00:00
generate_inmemory_RSA_master_and_subkeys() {
TRACE "Under oem-factory-reset:generate_inmemory_RSA_master_and_subkeys"
echo "Generating GPG key material in memory:"
echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits master key..."
# Generate GPG master key
{
echo "Key-Type: RSA"
echo "Key-Length: ${RSA_KEY_LENGTH}"
echo "Key-Usage: sign"
echo "Name-Real: ${GPG_USER_NAME}"
echo "Name-Comment: ${GPG_USER_COMMENT}"
echo "Name-Email: ${GPG_USER_MAIL}"
echo "Expire-Date: 0"
echo "Passphrase: ${ADMIN_PIN}"
echo "%commit"
2023-10-26 20:50:10 +00:00
} | gpg --command-fd=0 --status-fd=1 --batch --gen-key >/tmp/gpg_card_edit_output 2>&1
2023-10-18 17:15:48 +00:00
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG Key generation failed!\n\n$ERROR"
fi
echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits signing subkey..."
# Add signing subkey
{
echo addkey
echo 4 # RSA (sign only)
echo ${RSA_KEY_LENGTH}
echo 0 # no expiration
echo ${ADMIN_PIN}
echo y # confirm
echo save
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG Key signing subkey generation failed!\n\n$ERROR"
fi
echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits encryption subkey..."
#Add encryption subkey
{
echo addkey
echo 6 # RSA (encrypt only)
echo ${RSA_KEY_LENGTH}
echo 0 # no expiration
echo ${ADMIN_PIN}
echo y # confirm
echo save
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG Key encryption subkey generation failed!\n\n$ERROR"
fi
echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits authentication subkey..."
#Add authentication subkey
{
#Authentication subkey needs gpg in expert mode to select RSA custom mode (8)
# in order to disable encryption and signing capabilities of subkey
# and then enable authentication capability
echo addkey
echo 8 # RSA (own capabilite)
echo S # disable signing capability
echo E # disable encryption capability
echo A # enable authentication capability
echo Q # quit
echo ${RSA_KEY_LENGTH}
echo 0 # no expiration
echo ${ADMIN_PIN}
echo y # confirm
echo save
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --expert --edit-key "${GPG_USER_MAIL}" \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG Key authentication subkey generation failed!\n\n$ERROR"
fi
DEBUG "Setting public key to ultimate trust..."
#Set the public key to the ultimate trust
{
echo trust
echo 5 # ultimate
echo y # confirm
echo save
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG Key setting public key to ultimate trust failed!\n\n$ERROR"
fi
}
2023-10-25 21:01:01 +00:00
#Generate a gpg master key: no expiration date, p256 key (ECC)
#This key will be used to sign 3 subkeys: encryption, authentication and signing
2023-10-26 20:50:10 +00:00
#The master key and subkeys will be copied to backup, and the subkeys moved from memory keyring to the smartcard
2023-10-25 21:01:01 +00:00
generate_inmemory_p256_master_and_subkeys() {
2023-10-26 20:50:10 +00:00
TRACE "Under oem-factory-reset:generate_inmemory_p256_master_and_subkeys"
2023-10-25 21:01:01 +00:00
2023-10-26 20:50:10 +00:00
echo "Generating GPG p256 bits master key..."
{
echo "Key-Type: ECDSA"
echo "Key-Curve: nistp256"
echo "Key-Usage: cert"
echo "Name-Real: ${GPG_USER_NAME}"
echo "Name-Comment: ${GPG_USER_COMMENT}"
echo "Name-Email: ${GPG_USER_MAIL}"
echo "Expire-Date: 0"
echo "%commit"
} | gpg --expert --batch --pinentry-mode=loopback --passphrase=<(echo -n "${ADMIN_PIN}") --generate-key \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG p256 Key generation failed!\n\n$ERROR"
fi
2023-10-25 21:01:01 +00:00
2023-10-26 20:50:10 +00:00
#Keep Master key fingerprint for add key calls
MASTER_KEY_FP=$(gpg --list-secret-keys --with-colons | grep fpr | cut -d: -f10)
2023-10-25 21:01:01 +00:00
2023-10-26 20:50:10 +00:00
echo "Generating GPG nistp256 signing subkey..."
2023-10-25 21:01:01 +00:00
{
echo addkey
2023-10-26 20:50:10 +00:00
echo 11 # ECC own set capability
echo Q # sign already present
echo 3 # P-256
echo 0 # no expiration
} | gpg --expert --command-fd=0 --status-fd=1 --passphrase=<(echo -n "${ADMIN_PIN}") --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1
2023-10-25 21:01:01 +00:00
if [ $? -ne 0 ]; then
ERROR_MSG=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "Failed to add ECC nistp256 signing key to master key\n\n${ERROR_MSG}"
fi
2023-10-26 20:50:10 +00:00
echo "Generating GPG nistp256 encryption subkey..."
2023-10-25 21:01:01 +00:00
{
echo addkey
echo 12# ECC encrypt only
2023-10-26 20:50:10 +00:00
echo E # encrypt already present
echo 3 # P-256
echo 0 # no expiration
} | gpg --expert --command-fd=0 --status-fd=1 --passphrase=<(echo -n "${ADMIN_PIN}") --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1
2023-10-25 21:01:01 +00:00
if [ $? -ne 0 ]; then
ERROR_MSG=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "Failed to add ECC nistp256 encryption key to master key\n\n${ERROR_MSG}"
fi
2023-10-26 20:50:10 +00:00
echo "Generating GPG nistp256 authentication subkey..."
2023-10-25 21:01:01 +00:00
{
echo addkey
2023-10-26 20:50:10 +00:00
echo 11 # ECC own set capability
echo S # deactivate sign
echo A # activate auth
echo Q # Quit
echo 3 # P-256
echo 0 # no expiration
} | gpg --expert --command-fd=0 --status-fd=1 --passphrase=<(echo -n "${ADMIN_PIN}") --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1
2023-10-25 21:01:01 +00:00
if [ $? -ne 0 ]; then
ERROR_MSG=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "Failed to add ECC nistp256 authentication key to master key\n\n${ERROR_MSG}"
fi
}
2023-10-18 17:15:48 +00:00
#Function to move current gpg keyring subkeys to card (keytocard)
# This is aimed to be used after having generated master key and subkeys in memory and having backuped them to a LUKS container
# This function will keytocard the subkeys from the master key in the keyring
# The master key will be kept in the keyring
# The master key was already used to sign the subkeys, so it is not needed anymore
# Delete the master key from the keyring once key to card is done (already backuped on LUKS private partition)
keytocard_subkeys_to_smartcard() {
TRACE "Under oem-factory-reset:keytocard_subkeys_to_smartcard"
#make sure usb ready and usb dongle ready to communicate with
enable_usb
enable_usb_storage
gpg --card-status >/dev/null 2>&1 || die "Error getting GPG card status"
gpg_key_factory_reset
2023-10-26 20:50:10 +00:00
echo "Moving subkeys to smartcard..."
2023-10-18 17:15:48 +00:00
{
echo "key 1" #Select Signature key
echo "keytocard"
2023-10-26 20:50:10 +00:00
echo "1" # Signature key
echo "${ADMIN_PIN}" #Smartcard admin pin
echo "${ADMIN_PIN}" #Subkey PIN
echo "0" #No expiration date
2023-10-18 17:15:48 +00:00
echo "key 1"
echo "key 2"
echo "keytocard"
echo "2" # Encryption key
2023-10-26 20:50:10 +00:00
echo "${ADMIN_PIN}"
echo "${ADMIN_PIN}"
2023-10-18 17:15:48 +00:00
echo "key 2"
echo "key 3"
echo "keytocard"
echo "3" # Authentication key
2023-10-26 20:50:10 +00:00
echo "${ADMIN_PIN}"
echo "${ADMIN_PIN}"
2023-10-18 17:15:48 +00:00
echo "key 3"
echo "save"
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --expert --edit-key "${GPG_USER_MAIL}" \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG Key moving subkeys to smartcard failed!\n\n$ERROR"
fi
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:keytocard_subkeys_to_smartcard done"
2023-10-18 17:15:48 +00:00
}
#Whiptail prompt to disconnect any external USB storage device
prompt_disconnect_external_USB_storage_device() {
TRACE "Under oem-factory-reset:disconnect_external_USB_storage_device"
#Whiptail $BG_COLOR_WARNING warning about removing any external USB storage device currently connected
whiptail $BG_COLOR_WARNING --title 'WARNING: Please disconnect any external USB storage device' \
--msgbox "An external USB storage device will be WIPED next.\n\nPlease disconnect all external USB storage devices." 0 80 ||
die "Error displaying warning about removing any external USB storage device currently connected"
}
#Whiptail prompt to insert to be wiped thumb drive
prompt_insert_to_be_wiped_thumb_drive() {
TRACE "Under oem-factory-reset:prompt_insert_to_be_wiped_thumb_drive"
#Whiptail warning about having only desired to be wiped thumb drive inserted
whiptail $BG_COLOR_WARNING --title 'WARNING: Please insert the thumb drive to be wiped' \
--msgbox "The thumb drive will be WIPED next.\n\nPlease have connected only the thumb drive to be wiped." 0 80 ||
die "Error displaying warning about having only desired to be wiped thumb drive inserted"
}
#list blkid devices (removing partition numbers)
list_blkid_devices() {
TRACE "Under oem-factory-reset:list_blkid_devices"
blkid | cut -d: -f1 | sed 's/[0-9]$//'
}
#export master key and subkeys to thumbdrive's private LUKS contained partition
export_master_key_subkeys_and_revocation_key_to_private_LUKS_container() {
TRACE "Under oem-factory-reset:export_master_key_subkeys_and_revocation_key_to_private_LUKS_container"
#Sanity check on passed arguments
while [ $# -gt 0 ]; do
case "$1" in
--mode)
mode="$2"
shift
shift
;;
--device)
device="$2"
shift
shift
;;
--mountpoint)
mountpoint="$2"
shift
shift
;;
--pass)
2023-10-26 20:50:10 +00:00
pass="${2}"
2023-10-18 17:15:48 +00:00
shift
shift
;;
*)
die "Error: unknown argument: $1"
;;
esac
done
mount-usb --mode "$mode" --device "$device" --mountpoint "$mountpoint" --pass "$pass" || die "Error mounting thumb drive's private partition"
#Export master key and subkeys to thumb drive
DEBUG "Exporting master key and subkeys to private LUKS container's partition..."
2023-10-26 20:50:10 +00:00
DEBUG "TODO DELETE THIS pass= ${pass} here"
gpg --export-secret-key --armor --pinentry-mode loopback --passphrase=<(echo -n "${pass}") "${GPG_USER_MAIL}" >"$mountpoint"/privkey.sec ||
2023-10-18 17:15:48 +00:00
die "Error exporting master key to private LUKS container's partition"
2023-10-26 20:50:10 +00:00
gpg --export-secret-subkeys --armor --pinentry-mode loopback --passphrase=<(echo -n "${pass}") "${GPG_USER_MAIL}" >"$mountpoint"/subkeys.sec ||
2023-10-18 17:15:48 +00:00
die "Error exporting subkeys to private LUKS container's partition"
#copy whole keyring to thumb drive, including revocation key and trust database
cp -af ~/.gnupg "$mountpoint"/.gnupg || die "Error copying whole keyring to private LUKS container's partition"
#Unmount private LUKS container's mount point
umount "$mountpoint" || die "Error unmounting private LUKS container's mount point"
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:export_master_key_subkeys_and_revocation_key_to_private_LUKS_container done"
2023-10-18 17:15:48 +00:00
}
#Export public key to thumb drive's public partition
export_public_key_to_thumbdrive_public_partition() {
TRACE "Under oem-factory-reset:export_public_key_to_thumbdrive_public_partition"
#Sanity check on passed arguments
while [ $# -gt 0 ]; do
case "$1" in
--mode)
mode="$2"
shift
shift
;;
--device)
device="$2"
shift
shift
;;
--mountpoint)
mountpoint="$2"
shift
shift
;;
*)
die "Error: unknown argument: $1"
;;
esac
done
#pass non-empty arguments to --pass, --mountpoint, --device, --mode
mount-usb --device "$device" --mode "$mode" --mountpoint "$mountpoint" || die "Error mounting thumb drive's public partition"
gpg --export --armor "${GPG_USER_MAIL}" >"$mountpoint"/pubkey.asc || die "Error exporting public key to thumb drive's public partition"
umount "$mountpoint" || die "Error unmounting thumb drive's public partition"
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:export_public_key_to_thumbdrive_public_partition done"
2023-10-18 17:15:48 +00:00
}
#Wipe a thumb drive and export master key and subkeys to it
wipe_thumb_drive_and_copy_gpg_key_material() {
TRACE "Under oem-factory-reset:wipe_thumb_drive_and_copy_gpg_key_material"
prompt_disconnect_external_USB_storage_device
actual_devices=$(list_blkid_devices)
#enable usb storage
enable_usb
enable_usb_storage
prompt_insert_to_be_wiped_thumb_drive
new_devices=$(list_blkid_devices)
thumb_drive=$(echo "$new_devices" | grep -v "$actual_devices" | uniq)
if [ -z "$thumb_drive" ]; then
whiptail_error_die "No new thumb drive detected! Aborting."
fi
select_luks_container_size_percent
#Wipe thumb drive with a LUKS container of size $(cat /tmp/luks_container_size_percent)
2023-10-26 20:50:10 +00:00
prepare_thumb_drive --device "$thumb_drive" --percentage "$(cat /tmp/luks_container_size_percent)" --pass "${ADMIN_PIN}"
2023-10-18 17:15:48 +00:00
#Export master key and subkeys to thumb drive first partition
2023-10-26 20:50:10 +00:00
export_master_key_subkeys_and_revocation_key_to_private_LUKS_container --mode rw --device "$thumb_drive"1 --mountpoint /media --pass "${ADMIN_PIN}"
2023-10-18 17:15:48 +00:00
#Export public key to thumb drive's public partition
export_public_key_to_thumbdrive_public_partition --mode rw --device "$thumb_drive"2 --mountpoint /media
2023-10-27 15:18:20 +00:00
TRACE "Under oem-factory-reset:wipe_thumb_drive_and_copy_gpg_key_material done"
2023-10-18 17:15:48 +00:00
}
gpg_key_factory_reset() {
TRACE "Under oem-factory-reset:gpg_key_factory_reset"
#enable usb storage
enable_usb
2019-08-15 18:36:05 +00:00
# Factory reset GPG card
2023-10-27 15:18:20 +00:00
echo "GPG factory reset of USB Security Dongle's smartcard..."
2019-08-15 18:36:05 +00:00
{
echo admin
echo factory-reset
echo y
echo yes
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
2023-10-18 17:15:48 +00:00
>/tmp/gpg_card_edit_output 2>&1
2019-08-15 18:36:05 +00:00
if [ $? -ne 0 ]; then
2023-10-18 17:15:48 +00:00
ERROR=$(cat /tmp/gpg_card_edit_output)
2019-08-15 18:36:05 +00:00
whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR"
fi
2020-08-05 09:49:06 +00:00
# If Nitrokey Storage is inserted, reset AES keys as well
2023-10-18 17:15:48 +00:00
if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then
2020-08-05 09:49:06 +00:00
/bin/hotp_verification regenerate ${ADMIN_PIN_DEF}
2020-06-30 16:29:42 +00:00
fi
2023-02-02 02:12:45 +00:00
# Toggle forced sig (good security practice, forcing PIN request for each signature request)
if gpg --card-status | grep "Signature PIN" | grep -q "not forced"; then
2023-10-18 17:15:48 +00:00
DEBUG "GPG toggling forcesig on since off..."
2023-02-02 02:12:45 +00:00
{
echo admin
echo forcesig
echo ${ADMIN_PIN_DEF}
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
2023-10-18 17:15:48 +00:00
>/tmp/gpg_card_edit_output 2>&1
2023-02-02 02:12:45 +00:00
if [ $? -ne 0 ]; then
2023-10-18 17:15:48 +00:00
ERROR=$(cat /tmp/gpg_card_edit_output)
2023-02-02 02:12:45 +00:00
whiptail_error_die "GPG Key forcesig toggle on failed!\n\n$ERROR"
fi
fi
2023-09-05 10:28:52 +00:00
# use p256 for key generation if requested
2023-10-18 17:15:48 +00:00
if [ "$GPG_ALGO" = "p256" ]; then
{
echo admin
echo key-attr
echo 2 # ECC
echo 3 # P-256
echo ${ADMIN_PIN_DEF}
echo 2 # ECC
echo 3 # P-256
echo ${ADMIN_PIN_DEF}
echo 2 # ECC
echo 3 # P-256
echo ${ADMIN_PIN_DEF}
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit --expert \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "Setting key to NIST-P256 in USB security dongle failed."
fi
2023-09-05 10:28:52 +00:00
# fallback to RSA key generation by default
2023-10-26 20:50:10 +00:00
elif [ "$GPG_ALGO" = "rsa" ]; then
2023-10-18 17:15:48 +00:00
DEBUG "GPG setting RSA key length to ${RSA_KEY_LENGTH} bits..."
# Set RSA key length
{
echo admin
echo key-attr
echo 1 # RSA
echo ${RSA_KEY_LENGTH} #Signing key size set to RSA_KEY_LENGTH
echo ${ADMIN_PIN_DEF}
echo 1 # RSA
echo ${RSA_KEY_LENGTH} #Encryption key size set to RSA_KEY_LENGTH
echo ${ADMIN_PIN_DEF}
echo 1 # RSA
echo ${RSA_KEY_LENGTH} #Authentication key size set to RSA_KEY_LENGTH
echo ${ADMIN_PIN_DEF}
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "Setting key attributed to RSA ${RSA_KEY_LENGTH} bits in USB security dongle failed."
fi
2023-10-26 20:50:10 +00:00
else
#Unknown GPG_ALGO
whiptail_error_die "Unknown GPG_ALGO: $GPG_ALGO"
2020-11-24 11:48:41 +00:00
fi
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:gpg_key_factory_reset done"
2023-10-18 17:15:48 +00:00
}
generate_OEM_gpg_keys() {
TRACE "Under oem-factory-reset:generate_OEM_gpg_keys"
2023-10-25 21:01:01 +00:00
2023-10-26 20:50:10 +00:00
#This function simply generates subkeys in smartcard following smarcard config from gpg_key_factory_reset
2023-10-27 15:18:20 +00:00
echo "Generating GPG keys in USB Security dongle's smartcard..."
2023-10-26 20:50:10 +00:00
{
echo admin
echo generate
echo n
echo ${ADMIN_PIN_DEF}
echo ${USER_PIN_DEF}
echo 0
echo ${GPG_USER_NAME}
echo ${GPG_USER_MAIL}
echo ${GPG_USER_COMMENT}
echo ${USER_PIN_DEF}
} | gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \
>/tmp/gpg_card_edit_output 2>&1
if [ $? -ne 0 ]; then
ERROR=$(cat /tmp/gpg_card_edit_output)
whiptail_error_die "GPG Key automatic keygen failed!\n\n$ERROR"
2019-08-15 18:36:05 +00:00
fi
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:generate_OEM_gpg_keys done"
2019-08-15 18:36:05 +00:00
}
2020-03-26 14:05:51 +00:00
2023-10-18 17:15:48 +00:00
gpg_key_change_pin() {
TRACE "Under oem-factory-reset:gpg_key_change_pin"
2023-10-27 15:18:20 +00:00
DEBUG "Changing GPG key PIN"
2019-11-07 19:01:49 +00:00
# 1 = user PIN, 3 = admin PIN
PIN_TYPE=$1
2023-10-27 15:18:20 +00:00
PIN_ORIG=${2}
PIN_NEW=${3}
2019-11-07 19:01:49 +00:00
# Change PIN
{
echo admin
echo passwd
echo ${PIN_TYPE}
echo ${PIN_ORIG}
echo ${PIN_NEW}
echo ${PIN_NEW}
echo q
echo q
} | gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \
2023-10-18 17:15:48 +00:00
>/tmp/gpg_card_edit_output 2>&1
2019-11-07 19:01:49 +00:00
if [ $? -ne 0 ]; then
2023-10-18 17:15:48 +00:00
ERROR=$(cat /tmp/gpg_card_edit_output | fold -s)
2019-11-07 19:01:49 +00:00
whiptail_error_die "GPG Key PIN change failed!\n\n$ERROR"
fi
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:gpg_key_change_pin done"
2019-11-07 19:01:49 +00:00
}
2019-08-15 18:36:05 +00:00
2023-10-18 17:15:48 +00:00
generate_checksums() {
TRACE "Under oem-factory-reset:generate_checksums"
2023-10-27 15:18:20 +00:00
2019-08-15 18:36:05 +00:00
# ensure /boot mounted
2023-10-18 17:15:48 +00:00
if ! grep -q /boot /proc/mounts; then
2019-08-15 18:36:05 +00:00
mount -o rw /boot || whiptail_error_die "Unable to mount /boot"
else
mount -o remount,rw /boot || whiptail_error_die "Unable to mount /boot"
fi
2022-04-13 18:24:59 +00:00
#Check if previous TPM Disk unlock Key was set
if [ -e /boot/kexec_key_devices.txt ]; then
TPM_DISK_ENCRYPTION_KEY_SET=1
fi
2019-08-15 18:36:05 +00:00
# clear any existing checksums/signatures
rm /boot/kexec* 2>/dev/null
# create Heads TPM counter
2023-10-18 17:15:48 +00:00
if [ "$CONFIG_TPM" = "y" ]; then
if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then
tpmr counter_create \
-pwdo "$TPM_PASS" \
-pwdc '' \
-la -3135106223 |
tee /tmp/counter ||
whiptail_error_die "Unable to create TPM counter"
TPM_COUNTER=$(cut -d: -f1 </tmp/counter)
# increment TPM counter
increment_tpm_counter $TPM_COUNTER >/dev/null 2>&1 ||
whiptail_error_die "Unable to increment tpm counter"
# create rollback file
sha256sum /tmp/counter-$TPM_COUNTER >/boot/kexec_rollback.txt 2>/dev/null ||
whiptail_error_die "Unable to create rollback file"
else
## needs to exist for initial call to unseal-hotp
echo "0" >/boot/kexec_hotp_counter
fi
2022-08-25 18:43:31 +00:00
fi
2019-08-15 18:36:05 +00:00
2022-04-13 18:24:59 +00:00
# set default boot option only if no TPM Disk Unlock Key previously set
if [ -z "$TPM_DISK_ENCRYPTION_KEY_SET" ]; then
set_default_boot_option
fi
2019-08-15 18:36:05 +00:00
# generate hashes
2022-12-31 17:41:24 +00:00
(
set -e -o pipefail
cd /boot
2023-10-18 17:15:48 +00:00
find ./ -type f ! -path './kexec*' -print0 |
xargs -0 sha256sum >/boot/kexec_hashes.txt 2>/dev/null
print_tree >/boot/kexec_tree.txt
2022-12-31 17:41:24 +00:00
)
[ $? -eq 0 ] || whiptail_error_die "Error generating kexec hashes"
2019-08-15 18:36:05 +00:00
2023-10-18 17:15:48 +00:00
param_files=$(find /boot/kexec*.txt)
[ -z "$param_files" ] &&
whiptail_error_die "No kexec parameter files to sign"
2019-08-15 18:36:05 +00:00
# sign kexec boot files
if sha256sum $param_files 2>/dev/null | gpg \
2023-10-18 17:15:48 +00:00
--pinentry-mode loopback \
2023-10-26 20:50:10 +00:00
--passphrase "${USER_PIN}" \
2023-10-18 17:15:48 +00:00
--digest-algo SHA256 \
--detach-sign \
-a \
>/boot/kexec.sig 2>/tmp/error; then
2019-08-15 18:36:05 +00:00
# successful - update the validated params
2023-10-18 17:15:48 +00:00
if ! check_config /boot >/dev/null 2>/tmp/error; then
2019-08-15 18:36:05 +00:00
cat /tmp/error
ret=1
else
ret=0
fi
else
cat /tmp/error
ret=1
fi
# done writing to /boot, switch back to RO
mount -o ro,remount /boot
2023-10-18 17:15:48 +00:00
if [ $ret = 1 ]; then
2020-07-10 22:29:43 +00:00
ERROR=$(tail -n 1 /tmp/error | fold -s)
2019-08-15 18:36:05 +00:00
whiptail_error_die "Error signing kexec boot files:\n\n$ERROR"
fi
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:generate_checksums done"
2019-08-15 18:36:05 +00:00
}
2023-10-18 17:15:48 +00:00
set_default_boot_option() {
TRACE "Under oem-factory-reset:set_default_boot_option"
2023-10-27 15:18:20 +00:00
2019-08-15 18:36:05 +00:00
option_file="/tmp/kexec_options.txt"
tmp_menu_file="/tmp/kexec/kexec_menu.txt"
hash_file="/boot/kexec_default_hashes.txt"
mkdir -p /tmp/kexec/
rm $option_file 2>/dev/null
# parse boot options from grub.cfg
2023-10-18 17:15:48 +00:00
for i in $(find /boot -name "grub.cfg"); do
kexec-parse-boot "/boot" "$i" >>$option_file
2019-08-15 18:36:05 +00:00
done
2020-07-29 03:24:32 +00:00
# FC29/30+ may use BLS format grub config files
# https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault
# only parse these if $option_file is still empty
if [ ! -s $option_file ] && [ -d "/boot/loader/entries" ]; then
2023-10-18 17:15:48 +00:00
for i in $(find /boot -name "grub.cfg"); do
kexec-parse-bls "/boot" "$i" "/boot/loader/entries" >>$option_file
done
2020-07-29 03:24:32 +00:00
fi
2023-10-18 17:15:48 +00:00
[ ! -s $option_file ] &&
whiptail_error_die "Failed to parse any boot options"
2019-08-15 18:36:05 +00:00
# sort boot options
2023-10-18 17:15:48 +00:00
sort -r $option_file | uniq >$tmp_menu_file
2019-08-15 18:36:05 +00:00
## save first option as default
2023-10-18 17:15:48 +00:00
entry=$(head -n 1 $tmp_menu_file | tail -1)
2019-08-15 18:36:05 +00:00
# clear existing default configs
rm "/boot/kexec_default.*.txt" 2>/dev/null
2020-07-29 05:18:57 +00:00
# get correct index for entry
index=$(grep -n "$entry" $option_file | cut -f1 -d ':')
2019-08-15 18:36:05 +00:00
# write new config
2023-10-18 17:15:48 +00:00
echo "$entry" >/boot/kexec_default.$index.txt
2019-08-15 18:36:05 +00:00
# validate boot option
2023-10-18 17:15:48 +00:00
(cd /boot && /bin/kexec-boot -b "/boot" -e "$entry" -f |
xargs sha256sum >$hash_file 2>/dev/null) ||
whiptail_error_die "Failed to create hashes of boot files"
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:set_default_boot_option done"
2019-08-15 18:36:05 +00:00
}
2023-10-18 17:15:48 +00:00
report_integrity_measurements() {
TRACE "Under oem-factory-reset:report_integrity_measurements"
2023-10-27 15:18:20 +00:00
2022-03-23 20:00:08 +00:00
#check for GPG key in keyring
2023-10-18 17:15:48 +00:00
GPG_KEY_COUNT=$(gpg -k 2>/dev/null | wc -l)
if [ "$GPG_KEY_COUNT" -ne 0 ]; then
2022-03-23 20:00:08 +00:00
# Check and report TOTP
# update the TOTP code every thirty seconds
2023-10-18 17:15:48 +00:00
date=$(date "+%Y-%m-%d %H:%M:%S %Z")
seconds=$(date "+%s")
half=$(expr \( "$seconds" % 60 \) / 30)
tpm2-tools: Change sense of CONFIG_TPM to mean any TPM, not just TPM1.
Most logic throughout Heads doesn't need to know TPM1 versus TPM2 (and
shouldn't, the differences should be localized). Some checks were
incorrect and are fixed by this change. Most checks are now unchanged
relative to master.
There are not that many places outside of tpmr that need to
differentiate TPM1 and TPM2. Some of those are duplicate code that
should be consolidated (seal-hotpkey, unseal-totp, unseal-hotp), and
some more are probably good candidates for abstracting in tpmr so the
business logic doesn't have to know TPM1 vs. TPM2.
Previously, CONFIG_TPM could be variously 'y', 'n', or empty. Now it
is always 'y' or 'n', and 'y' means "any TPM". Board configs are
unchanged, setting CONFIG_TPM2_TOOLS=y implies CONFIG_TPM=y so this
doesn't have to be duplicated and can't be mistakenly mismatched.
There were a few checks for CONFIG_TPM = n that only coincidentally
worked for TPM2 because CONFIG_TPM was empty (not 'n'). This test is
now OK, but the checks were also cleaned up to '!= "y"' for robustness.
Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
2023-02-22 21:30:07 +00:00
if [ "$CONFIG_TPM" != "y" ]; then
2022-03-23 20:00:08 +00:00
TOTP="NO TPM"
elif [ "$half" != "$last_half" ]; then
2023-10-18 17:15:48 +00:00
last_half=$half
TOTP=$(unseal-totp) >/dev/null 2>&1
2022-03-23 20:00:08 +00:00
fi
# Check and report on HOTP status
if [ -x /bin/hotp_verification ]; then
2023-10-18 17:15:48 +00:00
HOTP=$(unseal-hotp) >/dev/null 2>&1
2022-03-23 20:00:08 +00:00
enable_usb
2023-10-18 17:15:48 +00:00
if ! hotp_verification info >/dev/null 2>&1; then
2022-11-15 20:11:58 +00:00
whiptail $CONFIG_WARNING_BG_COLOR --title 'WARNING: Please insert your HOTP enabled USB Security dongle' --msgbox "Your HOTP enabled USB Security dongle was not detected.\n\nPlease remove it and insert it again." 0 80
2022-03-23 20:00:08 +00:00
fi
# Don't output HOTP codes to screen, so as to make replay attacks harder
hotp_verification check $HOTP
case "$?" in
2023-10-18 17:15:48 +00:00
0)
HOTP="Success"
;;
4)
HOTP="Invalid code"
MAIN_MENU_BG_COLOR=$CONFIG_ERROR_BG_COLOR
;;
*)
HOTP="Error checking code, Insert USB Security dongle and retry"
MAIN_MENU_BG_COLOR=$CONFIG_WARNING_BG_COLOR
;;
esac
else
HOTP='N/A'
2022-03-23 20:00:08 +00:00
fi
# Check for detached signed digest and report on /boot integrity status
check_config /boot force
TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt"
2023-10-18 17:15:48 +00:00
if (cd /boot && sha256sum -c "$TMP_HASH_FILE" >/tmp/hash_output); then
2022-03-23 20:00:08 +00:00
HASH="OK"
2023-10-18 17:15:48 +00:00
else
HASH="ALTERED"
2022-03-23 20:00:08 +00:00
fi
2023-10-18 17:15:48 +00:00
#Show results
whiptail $MAIN_MENU_BG_COLOR --title "Measured Integrity Report" --msgbox "$date\nTOTP: $TOTP | HOTP: $HOTP\n/BOOT INTEGRITY: $HASH\n\nPress OK to continue or Ctrl+Alt+Delete to reboot" 0 80
2022-03-23 20:00:08 +00:00
fi
2023-10-27 15:18:20 +00:00
TRACE "oem-factory-reset:report_integrity_measurements done"
2022-03-23 20:00:08 +00:00
}
2023-10-18 17:15:48 +00:00
usb_security_token_capabilities_check() {
2023-09-05 10:28:52 +00:00
TRACE "Under /bin/oem-factory-reset:usb_security_token_capabilities_check"
enable_usb
# ... first set board config preference
if [ -n "$CONFIG_GPG_ALGO" ]; then
GPG_ALGO=$CONFIG_GPG_ALGO
DEBUG "Setting GPG_ALGO to (board-)configured: $CONFIG_GPG_ALGO"
fi
# ... overwrite with usb-token capability
2023-10-25 21:01:01 +00:00
if lsusb | grep -q "20a0:42b2"; then
GPG_ALGO="p256"
DEBUG "Nitrokey 3 detected: Setting GPG_ALGO to: $GPG_ALGO"
fi
2023-09-05 10:28:52 +00:00
}
2022-03-23 20:00:08 +00:00
2019-08-15 18:36:05 +00:00
## main script start
# check for args
if [ "$1" != "" ]; then
title_text=$1
else
2022-03-10 14:55:08 +00:00
title_text="OEM Factory Reset / Re-Ownership"
2019-08-15 18:36:05 +00:00
fi
if [ "$2" != "" ]; then
bg_color=$2
else
bg_color=""
fi
# show warning prompt
tpm2-tools: Change sense of CONFIG_TPM to mean any TPM, not just TPM1.
Most logic throughout Heads doesn't need to know TPM1 versus TPM2 (and
shouldn't, the differences should be localized). Some checks were
incorrect and are fixed by this change. Most checks are now unchanged
relative to master.
There are not that many places outside of tpmr that need to
differentiate TPM1 and TPM2. Some of those are duplicate code that
should be consolidated (seal-hotpkey, unseal-totp, unseal-hotp), and
some more are probably good candidates for abstracting in tpmr so the
business logic doesn't have to know TPM1 vs. TPM2.
Previously, CONFIG_TPM could be variously 'y', 'n', or empty. Now it
is always 'y' or 'n', and 'y' means "any TPM". Board configs are
unchanged, setting CONFIG_TPM2_TOOLS=y implies CONFIG_TPM=y so this
doesn't have to be duplicated and can't be mistakenly mismatched.
There were a few checks for CONFIG_TPM = n that only coincidentally
worked for TPM2 because CONFIG_TPM was empty (not 'n'). This test is
now OK, but the checks were also cleaned up to '!= "y"' for robustness.
Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
2023-02-22 21:30:07 +00:00
if [ "$CONFIG_TPM" = "y" ]; then
2022-03-10 14:55:08 +00:00
TPM_STR=" * ERASE the TPM and own it with a password\n"
2020-04-23 23:36:03 +00:00
else
TPM_STR=""
fi
2019-08-15 18:36:05 +00:00
if ! whiptail --yesno "
2022-03-10 14:55:08 +00:00
This operation will automatically:\n
2020-04-23 23:36:03 +00:00
$TPM_STR
* ERASE any keys or passwords on the GPG smart card,\n
2022-03-10 14:55:08 +00:00
reset it to a factory state, generate new keys\n
2022-04-29 20:58:34 +00:00
and optionally set custom PIN(s)\n
2020-04-23 23:36:03 +00:00
* Add the new GPG key to the firmware and reflash it\n
* Sign all of the files in /boot with the new GPG key\n\n
It requires that you already have an OS installed on a\n
2022-03-10 14:55:08 +00:00
dedicated /boot partition. Do you wish to continue?" \
2023-10-18 17:15:48 +00:00
$HEIGHT $WIDTH $CONTINUE $CANCEL $CLEAR $bg_color --title "$title_text"; then
2019-08-15 18:36:05 +00:00
exit 1
fi
2022-03-23 20:00:08 +00:00
# We show current integrity measurements status and time
2023-10-26 20:50:10 +00:00
#TODO: readd prior of PR review request. Also make sure that check_config is called to check kexec.sig (detached signature validation)
2023-10-25 21:01:01 +00:00
#report_integrity_measurements
2022-03-23 20:00:08 +00:00
2023-09-05 10:28:52 +00:00
# Determine gpg algorithm to be used, based on available usb-token
usb_security_token_capabilities_check
2023-06-23 12:20:21 +00:00
use_defaults=n
if [ "$CONFIG_OEMRESET_OFFER_DEFAULTS" = y ]; then
2023-10-18 17:15:48 +00:00
echo -e -n "Would you like to use default configuration options?\nIf N, you will be prompted for each option [Y/n]: "
read -n 1 use_defaults
2023-06-23 12:20:21 +00:00
fi
2022-04-29 21:01:21 +00:00
if [ "$use_defaults" == "n" -o "$use_defaults" == "N" ]; then
2023-10-18 17:15:48 +00:00
#Give general guidance to user on how to answer prompts
2023-10-26 20:50:10 +00:00
echo
2023-10-18 17:15:48 +00:00
echo "The following questionnaire will help you to configure the security components of your system."
echo "You will be prompted for each option to answer a single letter at prompts (Y/n/m)."
echo "If you don't know what to answer, just press Enter to use default value which is shown between [] brackets as the uppercase letter."
# Re-ownership of encrypted disk key, content and passphrase
echo -e -n "\n\nWould you like to change the current LUKS Disk Recovery Key passphrase?\n (Highly recommended if you didn't install the Operating System yourself, so that past provisioned passphrase would not permit to access content.\n Note that without re-encrypting disk, a backuped header could be restored to access encrypted content with old passphrase) [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ]; then
luks_new_Disk_Recovery_Key_passphrase_desired=1
echo -e "\n"
fi
echo -e -n "Would you like to re-encrypt LUKS encrypted container and generate new Disk Recovery key?\n (Highly recommended if you didn't install the operating system yourself: this would prevent any LUKS backuped header to be restored to access encrypted data) [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ]; then
test_luks_current_disk_recovery_key_passphrase
luks_new_Disk_Recovery_Key_desired=1
echo -e "\n"
fi
#Prompt to ask if user wants to generate GPG key material in memory or on smartcard
echo -e -n "Would you like to generate GPG key material in (m)emory or (S)olely on the security element of the USB security dongle? [m/S]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "m" \
-o "$prompt_output" == "M" ] \
; then
GPG_GEN_KEY_IN_MEMORY=1
2023-10-26 20:50:10 +00:00
echo "Master key and subkeys will be generated in memory, backuped to dedicated LUKS container and then subkeys copied to smartcard."
2023-10-18 17:15:48 +00:00
else
GPG_GEN_KEY_IN_MEMORY=0
fi
# TODO: add LUKS container passphrase = ADMIN_PIN in security components provisioned
# Adapt message to be given to user in terms of security components that will be applied.
if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -o -n "$luks_new_Disk_Recovery_Key_passphrase" ]; then
CUSTOM_PASS_AFFECTED_COMPONENTS="LUKS Disk Recovery Key passphrase"
fi
if [ "$CONFIG_TPM" = "y" ]; then
CUSTOM_PASS_AFFECTED_COMPONENTS="$CUSTOM_PASS_AFFECTED_COMPONENTS
2023-10-23 17:13:39 +00:00
TPM Owner Password"
2023-10-18 17:15:48 +00:00
fi
CUSTOM_PASS_AFFECTED_COMPONENTS="$CUSTOM_PASS_AFFECTED_COMPONENTS
2022-03-23 19:55:42 +00:00
GPG Admin PIN
GPG User PIN"
2023-10-18 17:15:48 +00:00
# Inform user of security components affected for the following prompts
echo -e "The following security components will be provisioned with defaults or chosen PINs/passwords:
2022-04-29 21:01:21 +00:00
$CUSTOM_PASS_AFFECTED_COMPONENTS\n"
2023-10-18 17:15:48 +00:00
# Prompt to change default passwords
echo -e -n "Would you like to set a single custom password that will be provisioned to previously stated security components? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ]; then
echo -e "\nThe chosen custom password must be between 8 and $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n"
echo
while [[ ${#CUSTOM_SINGLE_PASS} -lt 8 ]] || [[ ${#CUSTOM_SINGLE_PASS} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do
echo -e -n "Enter the custom password: "
read CUSTOM_SINGLE_PASS
done
echo
2023-10-26 20:50:10 +00:00
TPM_PASS=${CUSTOM_SINGLE_PASS}
USER_PIN=${CUSTOM_SINGLE_PASS}
ADMIN_PIN=${CUSTOM_SINGLE_PASS}
2023-10-18 17:15:48 +00:00
# Only set if user said desired. Matches rest of logic
if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
2023-10-26 20:50:10 +00:00
luks_new_Disk_Recovery_Key_passphrase=${CUSTOM_SINGLE_PASS}
2023-10-18 17:15:48 +00:00
fi
else
echo -e -n "Would you like to set distinct PINs/passwords to be provisioned to previously stated security components? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ]; then
echo -e "\nThey must be each at least 8 characters in length.\n"
echo
if [ "$CONFIG_TPM" = "y" ]; then
while [[ ${#TPM_PASS} -lt 8 ]]; do
2023-10-23 17:13:39 +00:00
echo -e -n "Enter desired TPM Owner Password: "
2023-10-18 17:15:48 +00:00
read TPM_PASS
done
fi
while [[ ${#ADMIN_PIN} -lt 8 ]] || [[ ${#ADMIN_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do
echo -e -n "\nThis PIN should be between 8 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n"
echo -e -n "Enter desired GPG Admin PIN: "
read ADMIN_PIN
done
while [[ ${#USER_PIN} -lt 8 ]] || [[ ${#USER_PIN} -gt 64 ]]; do
echo -e -n "\nThis PIN should be between 8 to 64 characters in length.\n"
echo -e -n "Enter desired GPG User PIN: "
read USER_PIN
done
echo
fi
fi
if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase" ]; then
# We catch here if changing LUKS Disk Recovery Key passphrase was desired
# but yet undone. This is if not being covered by the single password
echo -e "\nEnter desired replacement for current Disk Recovery Key passphrase (At least 8 characters long):"
while [[ ${#luks_new_Disk_Recovery_Key_passphrase} -lt 8 ]]; do
{
read -r luks_new_Disk_Recovery_Key_passphrase
}
done
#We test that current Disk Recovery Key passphrase is known prior of going further
test_luks_current_disk_recovery_key_passphrase
echo -e "\n"
fi
# Prompt to change default GnuPG key information
echo -e -n "Would you like to set custom user information for the GnuPG key? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ]; then
echo -e "\n\n"
echo -e "We will generate a GnuPG (PGP) keypair identifiable with the following text form:"
echo -e "Real Name (Comment) email@address.org"
echo -e "\nEnter your Real Name (Optional):"
read -r GPG_USER_NAME
echo -e "\nEnter your email@adress.org:"
read -r GPG_USER_MAIL
while ! $(expr "$GPG_USER_MAIL" : '.*@' >/dev/null); do
{
echo -e "\nEnter your email@address.org:"
read -r GPG_USER_MAIL
}
done
echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):"
read -r GPG_USER_COMMENT
while [[ ${#GPG_USER_COMMENT} -gt 60 ]]; do
{
echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):"
read -r GPG_USER_COMMENT
}
done
fi
fi
2022-04-29 21:01:21 +00:00
# If nothing is stored in custom variables, we set them to their defaults
2023-10-26 20:50:10 +00:00
if [ "$TPM_PASS" == "" ]; then TPM_PASS=${TPM_PASS_DEF}; fi
if [ "$USER_PIN" == "" ]; then USER_PIN=${USER_PIN_DEF}; fi
if [ "$ADMIN_PIN" == "" ]; then ADMIN_PIN=${ADMIN_PIN_DEF}; fi
2020-01-02 16:29:11 +00:00
2020-07-07 08:32:22 +00:00
## sanity check the USB, GPG key, and boot device before proceeding further
2023-10-18 17:15:48 +00:00
if [ "$GPG_GEN_KEY_IN_MEMORY" == "0" ]; then
# Prompt to insert USB drive if desired
echo -e -n "\nWould you like to export your public key to an USB drive? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ] \
; then
GPG_EXPORT=1
# mount USB over /media only if not already mounted
if ! grep -q /media /proc/mounts; then
# mount USB in rw
if ! mount-usb --mode rw 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Unable to mount USB on /media:\n\n${ERROR}"
fi
else
#/media already mounted, make sure it is in r+w mode
if ! mount -o remount,rw /media 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Unable to remount in read+write USB on /media:\n\n${ERROR}"
fi
fi
2022-03-10 14:55:08 +00:00
else
2023-10-18 17:15:48 +00:00
GPG_EXPORT=0
# needed for USB Security dongle below and is ensured via mount-usb in case of GPG_EXPORT=1
enable_usb
2020-07-07 08:32:22 +00:00
fi
fi
2019-08-15 18:36:05 +00:00
2023-10-18 17:15:48 +00:00
# ensure USB Security Dongle connected if GPG_GEN_KEY_IN_MEMORY=0
if [ "$GPG_GEN_KEY_IN_MEMORY" == "0" ]; then
echo -e "\nChecking for USB Security Dongle...\n"
enable_usb
if ! gpg --card-status >/dev/null 2>&1; then
whiptail_error "Can't access USB Security Dongle; \nPlease remove and reinsert, then press Enter."
if ! gpg --card-status >/dev/null 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Unable to detect USB Security Dongle:\n\n${ERROR}"
fi
2019-08-15 18:36:05 +00:00
fi
fi
2023-01-12 16:31:31 +00:00
assert_signable
2022-03-23 19:55:42 +00:00
# Action time...
2023-10-19 19:42:27 +00:00
# clear local keyring
2023-10-26 20:50:10 +00:00
rm -rf /.gnupg/* >/dev/null 2>&1 || true
# clear gpg-agent cache so that next gpg calls doesn't have past keyring in memory
killall gpg-agent >/dev/null 2>&1 || true
2023-10-19 19:42:27 +00:00
2019-08-15 18:36:05 +00:00
# detect and set /boot device
echo -e "\nDetecting and setting boot device...\n"
2023-10-18 17:15:48 +00:00
if ! detect_boot_device; then
SKIP_BOOT="y"
2019-08-15 18:36:05 +00:00
else
2023-10-18 17:15:48 +00:00
echo -e "Boot device set to $CONFIG_BOOT_DEV\n"
2019-08-15 18:36:05 +00:00
fi
# update configs
2020-10-23 23:38:30 +00:00
if [[ "$SKIP_BOOT" == "n" ]]; then
2023-10-18 17:15:48 +00:00
replace_config /etc/config.user "CONFIG_BOOT_DEV" "$CONFIG_BOOT_DEV"
combine_configs
2020-10-23 23:38:30 +00:00
fi
2019-08-15 18:36:05 +00:00
2022-05-03 20:14:51 +00:00
if [ -n "$luks_new_Disk_Recovery_Key_desired" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
2023-10-18 17:15:48 +00:00
#Reencryption of disk, disk recovery key and Disk Recovery Key passphrase change is requested
luks_reencrypt
luks_change_passphrase
2022-05-03 20:14:51 +00:00
elif [ -n "$luks_new_Disk_Recovery_Key_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
2023-10-18 17:15:48 +00:00
#Reencryption of disk was requested but not passphrase change
luks_reencrypt
2022-05-03 20:14:51 +00:00
elif [ -z "$luks_new_Disk_Recovery_Key_desired" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
2023-10-18 17:15:48 +00:00
#Passphrase change is requested without disk reencryption
luks_change_passphrase
2022-03-23 19:55:42 +00:00
fi
2022-03-10 14:55:08 +00:00
## reset TPM and set password
tpm2-tools: Change sense of CONFIG_TPM to mean any TPM, not just TPM1.
Most logic throughout Heads doesn't need to know TPM1 versus TPM2 (and
shouldn't, the differences should be localized). Some checks were
incorrect and are fixed by this change. Most checks are now unchanged
relative to master.
There are not that many places outside of tpmr that need to
differentiate TPM1 and TPM2. Some of those are duplicate code that
should be consolidated (seal-hotpkey, unseal-totp, unseal-hotp), and
some more are probably good candidates for abstracting in tpmr so the
business logic doesn't have to know TPM1 vs. TPM2.
Previously, CONFIG_TPM could be variously 'y', 'n', or empty. Now it
is always 'y' or 'n', and 'y' means "any TPM". Board configs are
unchanged, setting CONFIG_TPM2_TOOLS=y implies CONFIG_TPM=y so this
doesn't have to be duplicated and can't be mistakenly mismatched.
There were a few checks for CONFIG_TPM = n that only coincidentally
worked for TPM2 because CONFIG_TPM was empty (not 'n'). This test is
now OK, but the checks were also cleaned up to '!= "y"' for robustness.
Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
2023-02-22 21:30:07 +00:00
if [ "$CONFIG_TPM" = "y" ]; then
2023-10-18 17:15:48 +00:00
echo -e "\nResetting TPM...\n"
tpmr reset "$TPM_PASS" >/dev/null 2>/tmp/error
2022-08-25 18:43:31 +00:00
fi
if [ $? -ne 0 ]; then
2023-10-18 17:15:48 +00:00
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error resetting TPM:\n\n${ERROR}"
2019-08-15 18:36:05 +00:00
fi
# clear local keyring
rm /.gnupg/*.gpg 2>/dev/null
rm /.gnupg/*.kbx 2>/dev/null
gpg --list-keys >/dev/null 2>&1
2023-10-26 20:50:10 +00:00
#Generate keys in memory and copy to smartcard
2023-10-18 17:15:48 +00:00
if [ "$GPG_GEN_KEY_IN_MEMORY" == "1" ]; then
2023-10-25 21:01:01 +00:00
if [ "$GPG_ALGO" == "RSA" ]; then
# Generate GPG master key
generate_inmemory_RSA_master_and_subkeys
#TODO seperate wiping and thumb drive functions with proper validation
wipe_thumb_drive_and_copy_gpg_key_material
#TODO seperate setting config
2023-10-27 15:18:20 +00:00
set_user_config "CONFIG_HAVE_GPG_KEY_BACKUP" "y"
2023-10-25 21:01:01 +00:00
keytocard_subkeys_to_smartcard
elif [ "$GPG_ALGO" == "p256" ]; then
generate_inmemory_p256_master_and_subkeys
#TODO seperate wiping and thumb drive functions with proper validation
wipe_thumb_drive_and_copy_gpg_key_material
#TODO seperate setting config
2023-10-27 15:18:20 +00:00
2023-10-25 21:01:01 +00:00
keytocard_subkeys_to_smartcard
else
die "Unsupported GPG_ALGO: $GPG_ALGO"
fi
2023-10-18 17:15:48 +00:00
else
2023-10-26 20:50:10 +00:00
#Generate GPG key and subkeys on smartcard only
2023-10-18 17:15:48 +00:00
echo -e "\nResetting GPG Key...\n(this will take around 3 minutes...)\n"
gpg_key_factory_reset
generate_OEM_gpg_keys
fi
2019-08-15 18:36:05 +00:00
2023-10-19 19:42:27 +00:00
# Obtain GPG key ID
GPG_GEN_KEY=$(gpg --list-keys --with-colons | grep "^fpr" | cut -d: -f10 | head -n1)
#Where to export the public key
2020-02-19 20:15:27 +00:00
PUBKEY="/tmp/${GPG_GEN_KEY}.asc"
2023-10-19 19:42:27 +00:00
# export pubkey to file
if ! gpg --export --armor "$GPG_GEN_KEY" >"${PUBKEY}" 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "GPG Key gpg export to file failed!\n\n$ERROR"
fi
2023-10-26 20:50:10 +00:00
#Applying custom GPG PINs to the smartcard if they were provided
2023-10-27 15:18:20 +00:00
if [ "${USER_PIN}" != "" -o "${ADMIN_PIN}" != "" ]; then
2023-10-26 20:50:10 +00:00
echo -e "\nChanging default GPG Admin PIN\n"
2023-10-27 15:18:20 +00:00
gpg_key_change_pin "3" "${ADMIN_PIN_DEF}" "${ADMIN_PIN}"
2023-10-26 20:50:10 +00:00
echo -e "\nChanging default GPG User PIN\n"
2023-10-27 15:18:20 +00:00
gpg_key_change_pin "1" "${USER_PIN_DEF}" "${USER_PIN}"
2023-10-26 20:50:10 +00:00
fi
2020-07-07 09:16:18 +00:00
2023-10-26 20:50:10 +00:00
## export pubkey to USB
2023-10-27 15:18:20 +00:00
if [ "$GPG_EXPORT" != "0" ]; then
2023-10-26 20:50:10 +00:00
echo -e "\nExporting generated key to USB...\n"
# copy to USB
if ! cp "${PUBKEY}" "/media/${GPG_GEN_KEY}.asc" 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Key export error: unable to copy ${GPG_GEN_KEY}.asc to /media:\n\n$ERROR"
2020-07-07 08:32:22 +00:00
fi
2023-10-26 20:50:10 +00:00
mount -o remount,ro /media 2>/dev/null
fi
2019-08-15 18:36:05 +00:00
# ensure key imported locally
2023-10-18 17:15:48 +00:00
if ! cat "$PUBKEY" | gpg --import >/dev/null 2>/tmp/error; then
2020-07-10 22:29:43 +00:00
ERROR=$(tail -n 1 /tmp/error | fold -s)
2019-08-15 18:36:05 +00:00
whiptail_error_die "Error importing GPG key:\n\n$ERROR"
fi
# update /.gnupg/trustdb.gpg to ultimately trust all user provided public keys
2023-10-18 17:15:48 +00:00
if ! gpg --list-keys --fingerprint --with-colons 2>/dev/null |
sed -E -n -e 's/^fpr:::::::::([0-9A-F]+):$/\1:6:/p' |
gpg --import-ownertrust >/dev/null 2>/tmp/error; then
2020-07-10 22:29:43 +00:00
ERROR=$(tail -n 1 /tmp/error | fold -s)
2019-08-15 18:36:05 +00:00
whiptail_error_die "Error importing GPG ownertrust:\n\n$ERROR"
fi
2023-10-18 17:15:48 +00:00
if ! gpg --update-trust >/dev/null 2>/tmp/error; then
2020-07-10 22:29:43 +00:00
ERROR=$(tail -n 1 /tmp/error | fold -s)
2019-08-15 18:36:05 +00:00
whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR"
fi
2023-10-19 19:42:27 +00:00
2023-10-27 15:18:20 +00:00
# Do not attempt to flash the key to ROM if we are running in QEMU based on CONFIG_BOARD_NAME matching glob pattern containing qemu-*
# We check for qemu-* instead of ^qemu- because CONFIG_BOARD_NAME could be renamed to UNTESTED-qemu-* in a probable future
if [[ "$CONFIG_BOARD_NAME" == qemu-* ]]; then
warn "Skipping flash of GPG key to ROM because we are running in QEMU"
warn "Please review documentation md file under board directory on your building machine, and review board config options in .config file to enable configuration options you want to test in QEMU until internal flashing support is integrated."
else
#We are not running in QEMU, so flash the key to ROM
## flash generated key to ROM
# TODO: would be nice if we warned users that qemu boards will fail here and tell them what to do
echo -e "\nReading current firmware...\n(this will take a minute or two)\n"
/bin/flash.sh -r /tmp/oem-setup.rom >/dev/null 2>/tmp/error
if [ ! -s /tmp/oem-setup.rom ]; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error reading current firmware:\n\n$ERROR"
fi
# clear any existing heads/gpg files from current firmware
for i in $(cbfs.sh -o /tmp/oem-setup.rom -l | grep -e "heads/"); do
cbfs.sh -o /tmp/oem-setup.rom -d "$i"
done
# add heads/gpg files to current firmware
if [ -e /.gnupg/pubring.kbx ]; then
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.kbx" -f /.gnupg/pubring.kbx
if [ -e /.gnupg/pubring.gpg ]; then
rm /.gnupg/pubring.gpg
fi
elif [ -e /.gnupg/pubring.gpg ]; then
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.gpg" -f /.gnupg/pubring.gpg
fi
if [ -e /.gnupg/trustdb.gpg ]; then
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/trustdb.gpg" -f /.gnupg/trustdb.gpg
fi
# persist user config changes (boot device)
if [ -e /etc/config.user ]; then
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/etc/config.user" -f /etc/config.user
fi
# flash updated firmware image
echo -e "\nAdding generated key to current firmware and re-flashing...\n"
if ! /bin/flash.sh /tmp/oem-setup.rom 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR"
fi
2019-08-15 18:36:05 +00:00
fi
## sign files in /boot and generate checksums
2020-10-23 23:38:30 +00:00
if [[ "$SKIP_BOOT" == "n" ]]; then
2023-10-18 17:15:48 +00:00
echo -e "\nSigning boot files and generating checksums...\n"
generate_checksums
2020-10-23 23:38:30 +00:00
fi
2019-08-15 18:36:05 +00:00
2022-03-23 19:55:42 +00:00
# Prepare whiptail output of provisioned secrets
if [ -z "$luks_new_Disk_Recovery_Key_passphrase" -o -z "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
2023-10-18 17:15:48 +00:00
luks_passphrase_changed=""
2022-03-23 19:55:42 +00:00
else
2023-10-18 17:15:48 +00:00
luks_passphrase_changed="LUKS Disk Recovery Key passphrase:\n
2022-03-23 19:55:42 +00:00
$luks_new_Disk_Recovery_Key_passphrase"
fi
tpm2-tools: Change sense of CONFIG_TPM to mean any TPM, not just TPM1.
Most logic throughout Heads doesn't need to know TPM1 versus TPM2 (and
shouldn't, the differences should be localized). Some checks were
incorrect and are fixed by this change. Most checks are now unchanged
relative to master.
There are not that many places outside of tpmr that need to
differentiate TPM1 and TPM2. Some of those are duplicate code that
should be consolidated (seal-hotpkey, unseal-totp, unseal-hotp), and
some more are probably good candidates for abstracting in tpmr so the
business logic doesn't have to know TPM1 vs. TPM2.
Previously, CONFIG_TPM could be variously 'y', 'n', or empty. Now it
is always 'y' or 'n', and 'y' means "any TPM". Board configs are
unchanged, setting CONFIG_TPM2_TOOLS=y implies CONFIG_TPM=y so this
doesn't have to be duplicated and can't be mistakenly mismatched.
There were a few checks for CONFIG_TPM = n that only coincidentally
worked for TPM2 because CONFIG_TPM was empty (not 'n'). This test is
now OK, but the checks were also cleaned up to '!= "y"' for robustness.
Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
2023-02-22 21:30:07 +00:00
if [ "$CONFIG_TPM" = "y" ]; then
2023-10-24 15:19:49 +00:00
tpm_owner_password_changed="
2023-10-26 20:50:10 +00:00
TPM Owner Password: ${TPM_PASS}\n"
2022-03-23 19:55:42 +00:00
else
2023-10-24 15:19:49 +00:00
tpm_owner_password_changed=""
2022-03-23 19:55:42 +00:00
fi
## Show to user current provisioned secrets prior of rebooting
2022-03-10 14:55:08 +00:00
whiptail --msgbox "
2022-03-23 19:55:42 +00:00
$luks_passphrase_changed
2023-10-24 15:19:49 +00:00
$tpm_owner_password_changed
2023-10-26 20:50:10 +00:00
GPG Admin PIN: ${ADMIN_PIN}\n
GPG User PIN: ${USER_PIN}\n\n" \
2022-03-10 14:55:08 +00:00
$HEIGHT $WIDTH --title "Provisioned secrets"
2019-08-15 18:36:05 +00:00
## all done -- reboot
whiptail --msgbox "
2022-03-10 14:55:08 +00:00
OEM Factory Reset / Re-Ownership has completed successfully\n\n
2019-08-15 18:36:05 +00:00
After rebooting, you will need to generate new TOTP/HOTP secrets\n
when prompted in order to complete the setup process.\n\n
2019-11-26 17:10:39 +00:00
Press Enter to reboot.\n" \
2022-03-10 14:55:08 +00:00
$HEIGHT $WIDTH --title "OEM Factory Reset / Re-Ownership Complete"
2019-08-15 18:36:05 +00:00
2022-03-23 19:55:42 +00:00
# Clean LUKS secrets
luks_secrets_cleanup
unset luks_passphrase_changed
2023-10-24 15:19:49 +00:00
unset tpm_owner_password_changed
2022-03-23 19:55:42 +00:00
2019-08-15 18:36:05 +00:00
reboot