mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-18 20:47:55 +00:00
5501cd0744
Before: [ 155.845101] DEBUG: gpg --pinentry-mode loopback --passphrase Please Change Me --digest-algo SHA256 --detach-sign -a After: [ 131.272954] DEBUG: gpg --pinentry-mode loopback --passphrase <hidden> --digest-algo SHA256 --detach-sign -a Signed-off-by: Thierry Laurion <insurgo@riseup.net>
1354 lines
53 KiB
Bash
Executable File
1354 lines
53 KiB
Bash
Executable File
#!/bin/bash
|
|
# Automated setup of TPM, GPG keys, and disk
|
|
|
|
set -o pipefail
|
|
|
|
## External files sourced
|
|
. /etc/functions
|
|
. /etc/gui_functions
|
|
. /etc/luks-functions
|
|
. /tmp/config
|
|
|
|
TRACE_FUNC
|
|
|
|
# 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"
|
|
HEIGHT="0"
|
|
WIDTH="80"
|
|
|
|
USER_PIN_DEF=123456
|
|
ADMIN_PIN_DEF=12345678
|
|
TPM_PASS_DEF=12345678
|
|
USER_PIN=""
|
|
ADMIN_PIN=""
|
|
TPM_PASS=""
|
|
GPG_GEN_KEY_IN_MEMORY="n"
|
|
GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n"
|
|
|
|
#Circumvent Librem Key/Nitrokey HOTP firmware bug https://github.com/osresearch/heads/issues/1167
|
|
MAX_HOTP_GPG_PIN_LENGTH=25
|
|
|
|
# What are the Security components affected by custom passwords
|
|
CUSTOM_PASS_AFFECTED_COMPONENTS=""
|
|
|
|
# Default GPG Algorithm is RSA
|
|
# p256 also supported (TODO: nk3 supports RSA 4096 in secure element in firmare v1.7.1. Switch!?
|
|
GPG_ALGO="RSA"
|
|
# Default RSA key length is 3072 bits for OEM key gen. 4096 are way longer to generate in smartcard
|
|
RSA_KEY_LENGTH=3072
|
|
|
|
#Override RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards until canokey fixes
|
|
if [[ "$CONFIG_BOARD_NAME" == qemu-* ]]; then
|
|
DEBUG "Overriding RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards"
|
|
RSA_KEY_LENGTH=2048
|
|
fi
|
|
|
|
GPG_USER_NAME="OEM Key"
|
|
GPG_KEY_NAME=$(date +%Y%m%d%H%M%S)
|
|
GPG_USER_MAIL="oem-${GPG_KEY_NAME}@example.com"
|
|
GPG_USER_COMMENT="OEM-generated key"
|
|
SKIP_BOOT="n"
|
|
|
|
## functions
|
|
|
|
die() {
|
|
|
|
local msg=$1
|
|
if [ -n "$msg" ]; then
|
|
echo -e "\n$msg"
|
|
fi
|
|
kill -s TERM $TOP_PID
|
|
exit 1
|
|
}
|
|
|
|
local_whiptail_error() {
|
|
local msg=$1
|
|
if [ "$msg" = "" ]; then
|
|
die "whiptail error: An error msg is required"
|
|
fi
|
|
whiptail_error --msgbox "${msg}\n\n" $HEIGHT $WIDTH --title "Error"
|
|
}
|
|
|
|
whiptail_error_die() {
|
|
local_whiptail_error "$@"
|
|
die
|
|
}
|
|
|
|
mount_boot() {
|
|
TRACE_FUNC
|
|
# Mount local disk if it is not already mounted.
|
|
# Added so that 'o' can be typed early at boot to enter directly into OEM Factory Reset
|
|
if ! grep -q /boot /proc/mounts; then
|
|
# try to mount if CONFIG_BOOT_DEV exists
|
|
if [ -e "$CONFIG_BOOT_DEV" ]; then
|
|
mount -o ro $CONFIG_BOOT_DEV /boot || die "Failed to mount $CONFIG_BOOT_DEV. Please change boot device under Configuration > Boot Device"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#Generate a gpg master key: no expiration date, ${RSA_KEY_LENGTH} bits
|
|
#This key will be used to sign 3 subkeys: encryption, authentication and signing
|
|
#The master key and subkeys will be copied to backup, and the subkeys moved from memory keyring to the smartcard
|
|
generate_inmemory_RSA_master_and_subkeys() {
|
|
TRACE_FUNC
|
|
|
|
echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits master key..."
|
|
# Generate GPG master key
|
|
{
|
|
echo "Key-Type: RSA" # RSA key
|
|
echo "Key-Length: ${RSA_KEY_LENGTH}" # RSA key length
|
|
echo "Key-Usage: sign" # RSA key usage
|
|
echo "Name-Real: ${GPG_USER_NAME}" # User name
|
|
echo "Name-Comment: ${GPG_USER_COMMENT}" # User comment
|
|
echo "Name-Email: ${GPG_USER_MAIL}" # User email
|
|
echo "Expire-Date: 0" # No expiration date
|
|
echo "Passphrase: ${ADMIN_PIN}" # Admin PIN
|
|
echo "%commit" # Commit changes
|
|
} | DO_WITH_DEBUG gpg --expert --batch --command-fd=0 --status-fd=1 --pinentry-mode=loopback --generate-key >/tmp/gpg_card_edit_output 2>&1
|
|
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 # add key in --edit-key mode
|
|
echo 4 # RSA (sign only)
|
|
echo ${RSA_KEY_LENGTH} # Signing key size set to RSA_KEY_LENGTH
|
|
echo 0 # No expiration date
|
|
echo ${ADMIN_PIN} # Local keyring admin pin
|
|
echo y # confirm
|
|
echo save # save changes and commit to keyring
|
|
} | DO_WITH_DEBUG 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 # add key in --edit-key mode
|
|
echo 6 # RSA (encrypt only)
|
|
echo ${RSA_KEY_LENGTH} # Encryption key size set to RSA_KEY_LENGTH
|
|
echo 0 # No expiration date
|
|
echo ${ADMIN_PIN} # Local keyring admin pin
|
|
echo y # confirm
|
|
echo save # save changes and commit to keyring
|
|
} | DO_WITH_DEBUG 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 # add key in --edit-key mode
|
|
echo 8 # RSA (set your own capabilities)
|
|
echo S # disable sign capability
|
|
echo E # disable encryption capability
|
|
echo A # enable authentication capability
|
|
echo Q # Quit
|
|
echo ${RSA_KEY_LENGTH} # Authentication key size set to RSA_KEY_LENGTH
|
|
echo 0 # No expiration date
|
|
echo ${ADMIN_PIN} # Local keyring admin pin
|
|
echo y # confirm
|
|
echo save # save changes and commit to keyring
|
|
} | DO_WITH_DEBUG 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
|
|
}
|
|
|
|
#Generate a gpg master key: no expiration date, p256 key (ECC)
|
|
#This key will be used to sign 3 subkeys: encryption, authentication and signing
|
|
#The master key and subkeys will be copied to backup, and the subkeys moved from memory keyring to the smartcard
|
|
generate_inmemory_p256_master_and_subkeys() {
|
|
TRACE_FUNC
|
|
|
|
echo "Generating GPG p256 bits master key..."
|
|
{
|
|
echo "Key-Type: ECDSA" # ECDSA key
|
|
echo "Key-Curve: nistp256" # ECDSA key curve
|
|
echo "Key-Usage: cert" # ECDSA key usage
|
|
echo "Name-Real: ${GPG_USER_NAME}" # User name
|
|
echo "Name-Comment: ${GPG_USER_COMMENT}" # User comment
|
|
echo "Name-Email: ${GPG_USER_MAIL}" # User email
|
|
echo "Passphrase: ${ADMIN_PIN}" # Local keyring admin pin
|
|
echo "Expire-Date: 0" # No expiration date
|
|
echo "%commit" # Commit changes
|
|
} | DO_WITH_DEBUG gpg --expert --batch --command-fd=0 --status-fd=1 --pinentry-mode=loopback --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
|
|
|
|
#Keep Master key fingerprint for add key calls
|
|
MASTER_KEY_FP=$(gpg --list-secret-keys --with-colons | grep fpr | cut -d: -f10)
|
|
|
|
echo "Generating GPG nistp256 signing subkey..."
|
|
{
|
|
echo addkey # add key in --edit-key mode
|
|
echo 11 # ECC own set capability
|
|
echo Q # sign already present, do not modify
|
|
echo 3 # P-256
|
|
echo 0 # No validity/expiration date
|
|
echo ${ADMIN_PIN} # Local keyring admin pin
|
|
echo save # save changes and commit to keyring
|
|
} | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1
|
|
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
|
|
|
|
echo "Generating GPG nistp256 encryption subkey..."
|
|
{
|
|
echo addkey
|
|
echo 12 # ECC own set capability
|
|
echo Q # Quit
|
|
echo 3 # P-256
|
|
echo 0 # No validity/expiration date
|
|
echo ${ADMIN_PIN} # Local keyring admin pin
|
|
echo save # save changes and commit to keyring
|
|
} | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1
|
|
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
|
|
|
|
echo "Generating GPG nistp256 authentication subkey..."
|
|
{
|
|
echo addkey # add key in --edit-key mode
|
|
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
|
|
echo ${ADMIN_PIN} # Local keyring admin pin
|
|
echo save # save changes and commit to keyring
|
|
} | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1
|
|
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
|
|
|
|
}
|
|
|
|
#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 backed up 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 backed up on LUKS private partition)
|
|
keytocard_subkeys_to_smartcard() {
|
|
TRACE_FUNC
|
|
|
|
#make sure usb ready and USB Security 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
|
|
|
|
echo "Moving subkeys to smartcard..."
|
|
{
|
|
echo "key 1" #Toggle on Signature key in --edit-key mode on local keyring
|
|
echo "keytocard" #Move Signature key to smartcard
|
|
echo "1" #Select Signature key key slot on smartcard
|
|
echo "${ADMIN_PIN}" #Local keyring Subkey PIN
|
|
echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN
|
|
echo "0" #No expiration date
|
|
echo "key 1" #Toggle off Signature key
|
|
echo "key 2" #Toggle on Encryption key
|
|
echo "keytocard" #Move Encryption key to smartcard
|
|
echo "2" #Select Encryption key key slot on smartcard
|
|
echo "${ADMIN_PIN}" #Local keyring Subkey PIN
|
|
echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN
|
|
echo "key 2" #Toggle off Encryption key
|
|
echo "key 3" #Toggle on Authentication key
|
|
echo "keytocard" #Move Authentication key to smartcard
|
|
echo "3" #Select Authentication key slot on smartcard
|
|
echo "${ADMIN_PIN}" #Local keyring Subkey PIN
|
|
echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN
|
|
echo "key 3" #Toggle off Authentication key
|
|
echo "save" #Save changes and commit to keyring
|
|
} | DO_WITH_DEBUG gpg --expert --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 moving subkeys to smartcard failed!\n\n$ERROR"
|
|
fi
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
#Whiptail prompt to insert to be wiped thumb drive
|
|
prompt_insert_to_be_wiped_thumb_drive() {
|
|
TRACE_FUNC
|
|
#Whiptail warning about having only desired to be wiped thumb drive inserted
|
|
whiptail_warning --title 'WARNING: Please insert the thumb drive to be wiped' \
|
|
--msgbox "The thumb drive will be WIPED next.\n\nPlease connect only the thumb drive to be wiped and disconnect others." 0 80 ||
|
|
die "Error displaying warning about having only desired to be wiped thumb drive inserted"
|
|
}
|
|
|
|
#export master key and subkeys to thumbdrive's private LUKS contained partition
|
|
export_master_key_subkeys_and_revocation_key_to_private_LUKS_container() {
|
|
TRACE_FUNC
|
|
|
|
#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)
|
|
pass="${2}"
|
|
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..."
|
|
|
|
gpg --export-secret-key --armor --pinentry-mode loopback --passphrase="${pass}" "${GPG_USER_MAIL}" >"$mountpoint"/privkey.sec ||
|
|
die "Error exporting master key to private LUKS container's partition"
|
|
gpg --export-secret-subkeys --armor --pinentry-mode loopback --passphrase="${pass}" "${GPG_USER_MAIL}" >"$mountpoint"/subkeys.sec ||
|
|
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"
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
#Export public key to thumb drive's public partition
|
|
export_public_key_to_thumbdrive_public_partition() {
|
|
TRACE_FUNC
|
|
|
|
#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"
|
|
#TODO: reuse "Obtain GPG key ID" so that pubkey on public thumb drive partition is named after key ID
|
|
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"
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
# Select thumb drive and LUKS container size for GPG key export
|
|
# Sets variables containing selections:
|
|
# - thumb_drive
|
|
# - thumb_drive_luks_percent
|
|
select_thumb_drive_for_key_material() {
|
|
TRACE_FUNC
|
|
|
|
#enable usb storage
|
|
enable_usb
|
|
enable_usb_storage
|
|
|
|
prompt_insert_to_be_wiped_thumb_drive
|
|
|
|
#loop until user chooses a disk
|
|
thumb_drive=""
|
|
while [ -z "$thumb_drive" ]; do
|
|
#list usb storage devices
|
|
list_usb_storage disks >/tmp/usb_disk_list
|
|
# Abort if:
|
|
# - no disks found (prevent file_selector's nonsense prompt)
|
|
# - file_selector fails for any reason
|
|
# - user aborts (file_selector succeeds but FILE is empty)
|
|
if [ $(cat /tmp/usb_disk_list | wc -l) -gt 0 ] &&
|
|
file_selector --show-size "/tmp/usb_disk_list" "Select USB device to partition" &&
|
|
[ -n "$FILE" ]; then
|
|
# Obtain size of thumb drive to be wiped with fdisk
|
|
disk_size_bytes="$(blockdev --getsize64 "$FILE")"
|
|
if [ "$disk_size_bytes" -lt "$((128*1024*1024))" ]; then
|
|
warn "Thumb drive size is less than 128MB!"
|
|
warn "LUKS container needs to be at least 8MB!"
|
|
warn "If the next operation fails, try with a bigger thumb drive"
|
|
fi
|
|
|
|
select_luks_container_size_percent
|
|
thumb_drive_luks_percent="$(cat /tmp/luks_container_size_percent)"
|
|
|
|
if ! confirm_thumb_drive_format "$FILE" "$thumb_drive_luks_percent"; then
|
|
warn "Thumb drive wipe aborted by user!"
|
|
continue
|
|
fi
|
|
|
|
#User chose and confirmed a thumb drive and its size to be wiped
|
|
thumb_drive=$FILE
|
|
else
|
|
#No USB storage device detected
|
|
warn "No USB storage device detected! Aborting OEM Factory Reset / Re-Ownership"
|
|
sleep 3
|
|
die "No USB storage device detected! User decided to not wipe any thumb drive"
|
|
fi
|
|
done
|
|
thumb_drive_luks_percent="$(cat /tmp/luks_container_size_percent)"
|
|
}
|
|
|
|
#Wipe a thumb drive and export master key and subkeys to it
|
|
# $1 - thumb drive block device
|
|
# $2 - LUKS container percentage [1-99]
|
|
wipe_thumb_drive_and_copy_gpg_key_material() {
|
|
TRACE_FUNC
|
|
|
|
local thumb_drive thumb_drive_luks_percent
|
|
thumb_drive="$1"
|
|
thumb_drive_luks_percent="$2"
|
|
|
|
#Wipe thumb drive with a LUKS container of size $(cat /tmp/luks_container_size_percent)
|
|
prepare_thumb_drive "$thumb_drive" "$thumb_drive_luks_percent" "${ADMIN_PIN}"
|
|
#Export master key and subkeys to thumb drive first partition
|
|
export_master_key_subkeys_and_revocation_key_to_private_LUKS_container --mode rw --device "$thumb_drive"1 --mountpoint /media --pass "${ADMIN_PIN}"
|
|
#Export public key to thumb drive's public partition
|
|
export_public_key_to_thumbdrive_public_partition --mode rw --device "$thumb_drive"2 --mountpoint /media
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
gpg_key_factory_reset() {
|
|
TRACE_FUNC
|
|
|
|
#enable usb storage
|
|
enable_usb
|
|
|
|
# Factory reset GPG card
|
|
echo "GPG factory reset of USB Security Dongle's smartcard..."
|
|
{
|
|
echo admin # admin menu
|
|
echo factory-reset # factory reset smartcard
|
|
echo y # confirm
|
|
echo yes # confirm
|
|
} | DO_WITH_DEBUG 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 "GPG Key factory reset failed!\n\n$ERROR"
|
|
fi
|
|
# If Nitrokey Storage is inserted, reset AES keys as well
|
|
if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then
|
|
DEBUG "Nitrokey Storage detected, resetting AES keys..."
|
|
/bin/hotp_verification regenerate ${ADMIN_PIN_DEF}
|
|
DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle"
|
|
killall -9 scdaemon
|
|
fi
|
|
# 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
|
|
DEBUG "GPG toggling forcesig on since off..."
|
|
{
|
|
echo admin # admin menu
|
|
echo forcesig # toggle forcesig
|
|
echo ${ADMIN_PIN_DEF} # local keyring PIN
|
|
} | DO_WITH_DEBUG 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 "GPG Key forcesig toggle on failed!\n\n$ERROR"
|
|
fi
|
|
fi
|
|
# use p256 for key generation if requested
|
|
if [ "$GPG_ALGO" = "p256" ]; then
|
|
{
|
|
echo admin # admin menu
|
|
echo key-attr # key attributes
|
|
echo 2 # ECC
|
|
echo 3 # P-256
|
|
echo ${ADMIN_PIN_DEF} # local keyring PIN
|
|
echo 2 # ECC
|
|
echo 3 # P-256
|
|
echo ${ADMIN_PIN_DEF} # local keyring PIN
|
|
echo 2 # ECC
|
|
echo 3 # P-256
|
|
echo ${ADMIN_PIN_DEF} # local keyring PIN
|
|
} | DO_WITH_DEBUG gpg --expert --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 to NIST-P256 in USB Security Dongle failed."
|
|
fi
|
|
# fallback to RSA key generation by default
|
|
elif [ "$GPG_ALGO" = "RSA" ]; then
|
|
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} #Local keyring PIN
|
|
echo 1 # RSA
|
|
echo ${RSA_KEY_LENGTH} #Encryption key size set to RSA_KEY_LENGTH
|
|
echo ${ADMIN_PIN_DEF} #Local keyring PIN
|
|
echo 1 # RSA
|
|
echo ${RSA_KEY_LENGTH} #Authentication key size set to RSA_KEY_LENGTH
|
|
echo ${ADMIN_PIN_DEF} #Local keyring PIN
|
|
} | DO_WITH_DEBUG 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
|
|
else
|
|
#Unknown GPG_ALGO
|
|
whiptail_error_die "Unknown GPG_ALGO: $GPG_ALGO"
|
|
fi
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
generate_OEM_gpg_keys() {
|
|
TRACE_FUNC
|
|
|
|
#This function simply generates subkeys in smartcard following smarcard config from gpg_key_factory_reset
|
|
echo "Generating GPG keys in USB Security Dongle's smartcard..."
|
|
{
|
|
echo admin # admin menu
|
|
echo generate # generate keys
|
|
echo n # Do not export keys
|
|
echo ${ADMIN_PIN_DEF} # Default admin PIN since we just factory reset
|
|
echo ${USER_PIN_DEF} # Default user PIN since we just factory reset
|
|
echo 0 # No key expiration
|
|
echo ${GPG_USER_NAME} # User name
|
|
echo ${GPG_USER_MAIL} # User email
|
|
echo ${GPG_USER_COMMENT} # User comment
|
|
echo ${USER_PIN_DEF} # Default user PIN since we just factory reset
|
|
} | DO_WITH_DEBUG 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"
|
|
fi
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
gpg_key_change_pin() {
|
|
TRACE_FUNC
|
|
DEBUG "Changing GPG key PIN"
|
|
# 1 = user PIN, 3 = admin PIN
|
|
PIN_TYPE=$1
|
|
PIN_ORIG=${2}
|
|
PIN_NEW=${3}
|
|
# Change PIN
|
|
{
|
|
echo admin # admin menu
|
|
echo passwd # change PIN
|
|
echo ${PIN_TYPE} # 1 = user PIN, 3 = admin PIN
|
|
echo ${PIN_ORIG} # old PIN
|
|
echo ${PIN_NEW} # new PIN
|
|
echo ${PIN_NEW} # confirm new PIN
|
|
echo q # quit
|
|
echo q
|
|
} | DO_WITH_DEBUG 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 | fold -s)
|
|
whiptail_error_die "GPG Key PIN change failed!\n\n$ERROR"
|
|
fi
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
generate_checksums() {
|
|
TRACE_FUNC
|
|
|
|
# ensure /boot mounted
|
|
if ! grep -q /boot /proc/mounts; then
|
|
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
|
|
|
|
#Check if previous LUKS TPM Disk Unlock Key was set
|
|
if [ -e /boot/kexec_key_devices.txt ]; then
|
|
TPM_DISK_ENCRYPTION_KEY_SET=1
|
|
fi
|
|
|
|
# clear any existing checksums/signatures
|
|
rm /boot/kexec* 2>/dev/null
|
|
|
|
# create Heads TPM counter
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then
|
|
tpmr counter_create \
|
|
-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
|
|
fi
|
|
|
|
# set default boot option only if no LUKS TPM Disk Unlock Key previously set
|
|
if [ -z "$TPM_DISK_ENCRYPTION_KEY_SET" ]; then
|
|
set_default_boot_option
|
|
fi
|
|
|
|
DEBUG "Generating hashes"
|
|
(
|
|
set -e -o pipefail
|
|
cd /boot
|
|
find ./ -type f ! -path './kexec*' -print0 |
|
|
xargs -0 sha256sum >/boot/kexec_hashes.txt 2>/dev/null
|
|
print_tree >/boot/kexec_tree.txt
|
|
)
|
|
[ $? -eq 0 ] || whiptail_error_die "Error generating kexec hashes"
|
|
|
|
param_files=$(find /boot/kexec*.txt)
|
|
[ -z "$param_files" ] &&
|
|
whiptail_error_die "No kexec parameter files to sign"
|
|
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" -a "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "n" ]; then
|
|
#The local keyring used to generate in memory subkeys is still valid since no key has been moved to smartcard
|
|
#Local keyring passwd is ADMIN_PIN. We need to set USER_PIN to ADMIN_PIN to be able to sign next in this boot session
|
|
DEBUG "Setting GPG User PIN to GPG Admin PIN so local keyring can be used to detach-sign kexec files next"
|
|
USER_PIN=$ADMIN_PIN
|
|
fi
|
|
|
|
DEBUG "Detach-signing boot files under kexec.sig: ${param_files}"
|
|
if sha256sum $param_files 2>/dev/null | DO_WITH_DEBUG --mask-position 4 gpg \
|
|
--pinentry-mode loopback \
|
|
--passphrase "${USER_PIN}" \
|
|
--digest-algo SHA256 \
|
|
--detach-sign \
|
|
-a \
|
|
>/boot/kexec.sig 2>/tmp/error; then
|
|
# successful - update the validated params
|
|
if ! check_config /boot >/dev/null 2>/tmp/error; then
|
|
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
|
|
|
|
if [ $ret = 1 ]; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error signing kexec boot files:\n\n$ERROR"
|
|
fi
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
set_default_boot_option() {
|
|
TRACE_FUNC
|
|
|
|
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
|
|
for i in $(find /boot -name "grub.cfg"); do
|
|
kexec-parse-boot "/boot" "$i" >>$option_file
|
|
done
|
|
# 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
|
|
for i in $(find /boot -name "grub.cfg"); do
|
|
kexec-parse-bls "/boot" "$i" "/boot/loader/entries" >>$option_file
|
|
done
|
|
fi
|
|
[ ! -s $option_file ] &&
|
|
whiptail_error_die "Failed to parse any boot options"
|
|
|
|
# sort boot options
|
|
sort -r $option_file | uniq >$tmp_menu_file
|
|
|
|
## save first option as default
|
|
entry=$(head -n 1 $tmp_menu_file | tail -1)
|
|
|
|
# clear existing default configs
|
|
rm "/boot/kexec_default.*.txt" 2>/dev/null
|
|
|
|
# get correct index for entry
|
|
index=$(grep -n "$entry" $option_file | cut -f1 -d ':')
|
|
|
|
# write new config
|
|
echo "$entry" >/boot/kexec_default.$index.txt
|
|
|
|
# validate boot option
|
|
(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"
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
report_integrity_measurements() {
|
|
TRACE_FUNC
|
|
|
|
#check for GPG key in keyring
|
|
GPG_KEY_COUNT=$(gpg -k 2>/dev/null | wc -l)
|
|
if [ "$GPG_KEY_COUNT" -ne 0 ]; then
|
|
# Check and report TOTP
|
|
# update the TOTP code every thirty seconds
|
|
date=$(date "+%Y-%m-%d %H:%M:%S %Z")
|
|
seconds=$(date "+%s")
|
|
half=$(expr \( "$seconds" % 60 \) / 30)
|
|
if [ "$CONFIG_TPM" != "y" ]; then
|
|
TOTP="NO TPM"
|
|
elif [ "$half" != "$last_half" ]; then
|
|
last_half=$half
|
|
TOTP=$(unseal-totp) >/dev/null 2>&1
|
|
fi
|
|
|
|
# Check and report on HOTP status
|
|
if [ -x /bin/hotp_verification ]; then
|
|
HOTP="Unverified"
|
|
enable_usb
|
|
for attempt in 1 2 3; do
|
|
if ! hotp_verification info >/dev/null 2>&1; then
|
|
whiptail_warning --title "WARNING: Please insert your HOTP enabled USB Security Dongle (Attempt $attempt/3)" --msgbox "Your HOTP enabled USB Security Dongle was not detected.\n\nPlease remove it and insert it again." 0 80
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ $attempt -eq 3 ]; then
|
|
die "No HOTP enabled USB Security Dongle detected. Please disable 'CONFIG_HOTPKEY' in the board config and rebuild."
|
|
fi
|
|
|
|
# Don't output HOTP codes to screen, so as to make replay attacks harder
|
|
HOTP=$(unseal-hotp) >/dev/null 2>&1
|
|
hotp_verification check $HOTP
|
|
case "$?" in
|
|
0)
|
|
HOTP="Success"
|
|
;;
|
|
4)
|
|
HOTP="Invalid code"
|
|
BG_COLOR_MAIN_MENU="error"
|
|
;;
|
|
*)
|
|
HOTP="Error checking code, Insert USB Security Dongle and retry"
|
|
BG_COLOR_MAIN_MENU="warning"
|
|
;;
|
|
esac
|
|
else
|
|
HOTP='N/A'
|
|
fi
|
|
# Check for detached signed digest and report on /boot integrity status
|
|
check_config /boot force
|
|
TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt"
|
|
|
|
if (cd /boot && sha256sum -c "$TMP_HASH_FILE" >/tmp/hash_output); then
|
|
HASH="OK"
|
|
else
|
|
HASH="ALTERED"
|
|
fi
|
|
|
|
#Show results
|
|
whiptail_type $BG_COLOR_MAIN_MENU --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
|
|
fi
|
|
|
|
TRACE_FUNC
|
|
}
|
|
|
|
usb_security_token_capabilities_check() {
|
|
TRACE_FUNC
|
|
|
|
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
|
|
if lsusb | grep -q "20a0:42b2"; then
|
|
GPG_ALGO="p256"
|
|
DEBUG "Nitrokey 3 detected: Setting GPG_ALGO to: $GPG_ALGO"
|
|
fi
|
|
}
|
|
|
|
## main script start
|
|
|
|
# check for args
|
|
if [ "$1" != "" ]; then
|
|
title_text=$1
|
|
else
|
|
title_text="OEM Factory Reset / Re-Ownership"
|
|
fi
|
|
if [ "$2" != "" ]; then
|
|
bg_color=$2
|
|
else
|
|
bg_color=""
|
|
fi
|
|
|
|
# show warning prompt
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
TPM_STR=" * ERASE the TPM and own it with a password\n"
|
|
else
|
|
TPM_STR=""
|
|
fi
|
|
if ! whiptail_warning --yesno "
|
|
This operation will automatically:\n
|
|
$TPM_STR
|
|
* ERASE any keys or passwords on the GPG smart card,\n
|
|
reset it to a factory state, generate new keys\n
|
|
and optionally set custom PIN(s)\n
|
|
* 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
|
|
dedicated /boot partition. Do you wish to continue?" \
|
|
$HEIGHT $WIDTH $CONTINUE $CANCEL $CLEAR --title "$title_text"; then
|
|
exit 1
|
|
fi
|
|
|
|
#Make sure /boot is mounted if board config defines default
|
|
mount_boot
|
|
# We show current integrity measurements status and time
|
|
report_integrity_measurements
|
|
|
|
# Clear the screen
|
|
clear
|
|
|
|
#Prompt user for use of default configuration options
|
|
TRACE_FUNC
|
|
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
|
|
|
|
if [ "$use_defaults" == "n" -o "$use_defaults" == "N" ]; then
|
|
#Give general guidance to user on how to answer prompts
|
|
echo
|
|
echo "****************************************************"
|
|
echo "**** Factory Reset / Re-Ownership Questionnaire ****"
|
|
echo "****************************************************"
|
|
echo "The following questionnaire will help you configure the security components of your system."
|
|
echo "Each prompt requires a single letter answer: eg. (Y/n)."
|
|
echo -e "If you don't know what to answer, pressing Enter will select the default answer for that prompt: eg. Y, above.\n"
|
|
|
|
# Re-ownership of LUKS 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 configured passphrase would not permit to access content.\n Note that without re-encrypting disk, a backed up 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 LUKS Disk Recovery Key?\n (Highly recommended if you didn't install the operating system yourself: this would prevent any LUKS backed up 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
|
|
TRACE_FUNC
|
|
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 format an encrypted USB Thumb drive to store GPG key material?\n (Required to enable GPG authentication) [y/N]: "
|
|
read -n 1 prompt_output
|
|
echo
|
|
if [ "$prompt_output" == "y" \
|
|
-o "$prompt_output" == "Y" ] \
|
|
; then
|
|
GPG_GEN_KEY_IN_MEMORY="y"
|
|
echo " ++++ Master key and subkeys will be generated in memory, backed up to dedicated LUKS container +++"
|
|
echo -e -n "Would you like in-memory generated subkeys to be copied to USB Security Dongle's smartcard?\n (Highly recommended so the smartcard is used on daily basis and backup is kept safe, but not required) [Y/n]: "
|
|
read -n 1 prompt_output
|
|
echo
|
|
if [ "$prompt_output" == "n" \
|
|
-o "$prompt_output" == "N" ]; then
|
|
warn "Subkeys will NOT be copied to USB Security Dongle's smartcard"
|
|
warn "Your GPG key material backup thumb drive should be cloned to a second thumb drive for redundancy for production environements"
|
|
GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n"
|
|
else
|
|
echo "++++ Subkeys will be copied to USB Security Dongle's smartcard ++++"
|
|
warn "Please keep your GPG key material backup thumb drive safe"
|
|
GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="y"
|
|
fi
|
|
else
|
|
echo "GPG key material will be generated on USB Security Dongle's smartcard without backup"
|
|
GPG_GEN_KEY_IN_MEMORY="n"
|
|
GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n"
|
|
fi
|
|
|
|
# Dynamic messages to be given to user in terms of security components that will be applied
|
|
# based on previous answers
|
|
CUSTOM_PASS_AFFECTED_COMPONENTS="\n"
|
|
# 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\n"
|
|
fi
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
CUSTOM_PASS_AFFECTED_COMPONENTS+="TPM Owner Password\n"
|
|
fi
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then
|
|
CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG Key material backup passphrase (Same as GPG Admin PIN)\n"
|
|
fi
|
|
CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG Admin PIN\n"
|
|
# Only show GPG User PIN as affected component if GPG_GEN_KEY_IN_MEMORY not requested or GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then
|
|
CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG User PIN\n"
|
|
fi
|
|
|
|
# Inform user of security components affected for the following prompts
|
|
echo
|
|
echo -e "The following Security Components will be configured with defaults or further chosen PINs/passwords:
|
|
$CUSTOM_PASS_AFFECTED_COMPONENTS\n"
|
|
|
|
# Prompt to change default passwords
|
|
echo -e -n "Would you like to set a single custom password to all 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."
|
|
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
|
|
TPM_PASS=${CUSTOM_SINGLE_PASS}
|
|
USER_PIN=${CUSTOM_SINGLE_PASS}
|
|
ADMIN_PIN=${CUSTOM_SINGLE_PASS}
|
|
|
|
# Only set if user said desired
|
|
if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
|
|
luks_new_Disk_Recovery_Key_passphrase=${CUSTOM_SINGLE_PASS}
|
|
fi
|
|
else
|
|
echo -e -n "Would you like to set distinct PINs/passwords to configure previously stated security components? [y/N]: "
|
|
read -n 1 prompt_output
|
|
echo
|
|
if [ "$prompt_output" == "y" \
|
|
-o "$prompt_output" == "Y" ]; then
|
|
echo -e "\nThe TPM Owner Password and Admin PIN must be at least 8, the User PIN at least 6 characters in length.\n"
|
|
echo
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
while [[ ${#TPM_PASS} -lt 8 ]]; do
|
|
echo -e -n "Enter desired TPM Owner Password: "
|
|
read TPM_PASS
|
|
done
|
|
fi
|
|
while [[ ${#ADMIN_PIN} -lt 6 ]] || [[ ${#ADMIN_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do
|
|
echo -e -n "\nThis PIN should be between 6 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n"
|
|
echo -e -n "Enter desired GPG Admin PIN: "
|
|
read ADMIN_PIN
|
|
done
|
|
#USER PIN not required in case of GPG_GEN_KEY_IN_MEMORY not requested of if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is
|
|
# That is, if keys were NOT generated in memory (on smartcard only) or
|
|
# if keys were generated in memory but are to be moved from local keyring to smartcard
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then
|
|
while [[ ${#USER_PIN} -lt 6 ]] || [[ ${#USER_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do
|
|
echo -e -n "\nThis PIN should be between 6 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n"
|
|
echo -e -n "Enter desired GPG User PIN: "
|
|
read USER_PIN
|
|
done
|
|
fi
|
|
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 LUKS 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 LUKS Disk Recovery Key passphrase is known prior of going further
|
|
TRACE_FUNC
|
|
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
|
|
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then
|
|
select_thumb_drive_for_key_material
|
|
fi
|
|
fi
|
|
|
|
# If nothing is stored in custom variables, we set them to their defaults
|
|
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
|
|
|
|
## sanity check the USB, GPG key, and boot device before proceeding further
|
|
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" ]; 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
|
|
else
|
|
GPG_EXPORT=0
|
|
# needed for USB Security Dongle below and is ensured via mount-usb in case of GPG_EXPORT=1
|
|
enable_usb
|
|
fi
|
|
fi
|
|
|
|
# ensure USB Security Dongle connected if GPG_GEN_KEY_IN_MEMORY=n or if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD=y
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then
|
|
echo -e "\nChecking for USB Security Dongle...\n"
|
|
enable_usb
|
|
if ! gpg --card-status >/dev/null 2>&1; then
|
|
local_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
|
|
fi
|
|
|
|
#Now that USB Security Dongle is detected, we can check its capabilities and limitations
|
|
usb_security_token_capabilities_check
|
|
fi
|
|
|
|
assert_signable
|
|
|
|
# Action time...
|
|
|
|
# clear gpg-agent cache so that next gpg calls doesn't have past keyring in memory
|
|
killall gpg-agent >/dev/null 2>&1 || true
|
|
# clear local keyring
|
|
rm -rf /.gnupg/*.kbx /.gnupg/*.gpg >/dev/null 2>&1 || true
|
|
|
|
# detect and set /boot device
|
|
echo -e "\nDetecting and setting boot device...\n"
|
|
if ! detect_boot_device; then
|
|
SKIP_BOOT="y"
|
|
else
|
|
echo -e "Boot device set to $CONFIG_BOOT_DEV\n"
|
|
fi
|
|
|
|
# update configs
|
|
if [[ "$SKIP_BOOT" == "n" ]]; then
|
|
replace_config /etc/config.user "CONFIG_BOOT_DEV" "$CONFIG_BOOT_DEV"
|
|
combine_configs
|
|
fi
|
|
|
|
if [ -n "$luks_new_Disk_Recovery_Key_desired" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
|
|
#Reencryption of disk, LUKS Disk Recovery Key and LUKS Disk Recovery Key passphrase change is requested
|
|
luks_reencrypt
|
|
luks_change_passphrase
|
|
elif [ -n "$luks_new_Disk_Recovery_Key_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
|
|
#Reencryption of disk was requested but not passphrase change
|
|
luks_reencrypt
|
|
elif [ -z "$luks_new_Disk_Recovery_Key_desired" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
|
|
#Passphrase change is requested without disk reencryption
|
|
luks_change_passphrase
|
|
fi
|
|
|
|
## reset TPM and set password
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
echo -e "\nResetting TPM...\n"
|
|
tpmr reset "$TPM_PASS" >/dev/null 2>/tmp/error
|
|
fi
|
|
if [ $? -ne 0 ]; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error resetting TPM:\n\n${ERROR}"
|
|
fi
|
|
|
|
# clear local keyring
|
|
rm /.gnupg/*.gpg 2>/dev/null
|
|
rm /.gnupg/*.kbx 2>/dev/null
|
|
# initialize gpg wth empty keyring
|
|
gpg --list-keys >/dev/null 2>&1
|
|
|
|
#Generate keys in memory and copy to smartcard
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then
|
|
if [ "$GPG_ALGO" == "RSA" ]; then
|
|
# Generate GPG master key
|
|
generate_inmemory_RSA_master_and_subkeys
|
|
elif [ "$GPG_ALGO" == "p256" ]; then
|
|
generate_inmemory_p256_master_and_subkeys
|
|
else
|
|
die "Unsupported GPG_ALGO: $GPG_ALGO"
|
|
fi
|
|
wipe_thumb_drive_and_copy_gpg_key_material "$thumb_drive" "$thumb_drive_luks_percent"
|
|
set_user_config "CONFIG_HAVE_GPG_KEY_BACKUP" "y"
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then
|
|
keytocard_subkeys_to_smartcard
|
|
fi
|
|
else
|
|
#Generate GPG key and subkeys on smartcard only
|
|
echo -e "\nResetting USB Security Dongle's GPG smartcard...\n(this will take around 3 minutes...)\n"
|
|
gpg_key_factory_reset
|
|
generate_OEM_gpg_keys
|
|
fi
|
|
|
|
# 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
|
|
PUBKEY="/tmp/${GPG_GEN_KEY}.asc"
|
|
|
|
# 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
|
|
|
|
#Applying custom GPG PINs to the smartcard if they were provided
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then
|
|
#Only apply smartcard PIN change if smartcard only or if keytocard op is expected next
|
|
if [ "${USER_PIN}" != "" -o "${ADMIN_PIN}" != "" ]; then
|
|
echo -e "\nChanging default GPG Admin PIN\n"
|
|
gpg_key_change_pin "3" "${ADMIN_PIN_DEF}" "${ADMIN_PIN}"
|
|
echo -e "\nChanging default GPG User PIN\n"
|
|
gpg_key_change_pin "1" "${USER_PIN_DEF}" "${USER_PIN}"
|
|
fi
|
|
fi
|
|
|
|
## export pubkey to USB
|
|
if [ "$GPG_EXPORT" != "0" ]; then
|
|
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"
|
|
fi
|
|
mount -o remount,ro /media 2>/dev/null
|
|
fi
|
|
|
|
# ensure key imported locally
|
|
if ! cat "$PUBKEY" | DO_WITH_DEBUG gpg --import >/dev/null 2>/tmp/error; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error importing GPG key:\n\n$ERROR"
|
|
fi
|
|
# update /.gnupg/trustdb.gpg to ultimately trust all user provided public keys
|
|
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
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error importing GPG ownertrust:\n\n$ERROR"
|
|
fi
|
|
if ! gpg --update-trust >/dev/null 2>/tmp/error; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR"
|
|
fi
|
|
|
|
# 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 without internal flashing support."
|
|
warn "Please review boards/qemu*/qemu*.md documentation to extract public key from raw disk and inject at build time"
|
|
warn "Also review boards/qemu*/qemu*.config to tweak CONFIG_* options you might need to turn on/off manually at build time"
|
|
else
|
|
#We are not running in QEMU, so flash the key to ROM
|
|
|
|
## flash generated key to ROM
|
|
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
|
|
fi
|
|
|
|
## sign files in /boot and generate checksums
|
|
if [[ "$SKIP_BOOT" == "n" ]]; then
|
|
echo -e "\nSigning boot files and generating checksums...\n"
|
|
generate_checksums
|
|
fi
|
|
|
|
# passphrases set to be empty first
|
|
passphrases="\n"
|
|
|
|
# Prepare whiptail output of configured secrets
|
|
if [ -n "$luks_new_Disk_Recovery_Key_passphrase" -o -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
|
|
passphrases+="LUKS Disk Recovery Key passphrase: ${luks_new_Disk_Recovery_Key_passphrase}\n"
|
|
fi
|
|
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
passphrases+="TPM Owner Password: ${TPM_PASS}\n"
|
|
fi
|
|
|
|
#GPG PINs output
|
|
passphrases+="GPG Admin PIN: ${ADMIN_PIN}\n"
|
|
#USER PIN was configured if GPG_GEN_KEY_IN_MEMORY is not active or if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is active
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then
|
|
passphrases+="GPG User PIN: ${USER_PIN}\n"
|
|
fi
|
|
|
|
#If user decided to generate keys in memory, we add the thumb drive passphrase
|
|
if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then
|
|
passphrases+="GPG key material backup passphrase: ${ADMIN_PIN}\n"
|
|
fi
|
|
|
|
## Show to user current configured secrets prior of rebooting
|
|
whiptail --msgbox "
|
|
$(echo -e "$passphrases" | fold -w $((WIDTH-5)))" \
|
|
$HEIGHT $WIDTH --title "Configured secrets"
|
|
|
|
## all done -- reboot
|
|
whiptail --msgbox "
|
|
OEM Factory Reset / Re-Ownership has completed successfully\n\n
|
|
After rebooting, you will need to generate new TOTP/HOTP secrets\n
|
|
when prompted in order to complete the setup process.\n\n
|
|
Press Enter to reboot.\n" \
|
|
$HEIGHT $WIDTH --title "OEM Factory Reset / Re-Ownership Complete"
|
|
|
|
# Clean LUKS secrets
|
|
luks_secrets_cleanup
|
|
unset luks_passphrase_changed
|
|
unset tpm_owner_password_changed
|
|
|
|
reboot
|