#!/bin/bash
# Generate a random secret, seal it with the PCRs
# and write it to the TPM NVRAM.
#
# Pass in a hostname if you want to change it from the default string
#

. /etc/functions.sh

TRACE_FUNC

TPM_NVRAM_SPACE=4d47

HOST="$1"
if [ -z "$HOST" ]; then
	HOST="TPMTOTP"
fi
TPM_PASSWORD="$2"

TOTP_SECRET="/tmp/secret/totp.key"
TOTP_SEALED="/tmp/secret/totp.sealed"

STATUS "Generating TOTP secret"
dd \
	if=/dev/urandom \
	of="$TOTP_SECRET" \
	count=1 \
	bs=20 \
	2>/dev/null ||
	DIE "Unable to generate 20 random bytes"

secret="$(base32 <$TOTP_SECRET)"
pcrf="/tmp/secret/pcrf.bin"
INFO "TPM: Reading PCR values for TOTP sealing policy"
DEBUG "Sealing TOTP with actual state of PCR0-3"
tpmr.sh pcrread 0 "$pcrf"
tpmr.sh pcrread -a 1 "$pcrf"
tpmr.sh pcrread -a 2 "$pcrf"
tpmr.sh pcrread -a 3 "$pcrf"
DEBUG "Sealing TOTP with boot state of PCR4 (Going to recovery shell extends PCR4)"
# pcr 4 is expected to either:
#  zero on bare coreboot+linuxboot on x86 (boot mode: init)
#  already extended on ppc64 per BOOTKERNEL (skiboot) which boots heads.
# Read from event log to catch both cases, even when called from recovery shell.
tpmr.sh calcfuturepcr 4 >>"$pcrf"
# pcr 5 (kernel modules loaded) is not measured at sealing/unsealing of totp
DEBUG "Sealing TOTP neglecting PCR5 involvement (Dynamically loaded kernel modules are not firmware integrity attestation related)"
# pcr 6 (drive LUKS header) is not measured at sealing/unsealing of totp
DEBUG "Sealing TOTP without PCR6 involvement (LUKS header consistency is not firmware integrity attestation related)"
# pcr 7 is containing measurements of user injected stuff in cbfs
DEBUG "Sealing TOTP with actual state of PCR7 (User injected stuff in cbfs)"
tpmr.sh pcrread -a 7 "$pcrf"
#Make sure we clear the TPM Owner Passphrase from memory in case it failed to be used to seal TOTP

# if the board has TPM2 tools, check for the primary handle before
# attempting to seal; a missing handle is the most common reason for
# failure and we want to give the same message as unseal-totp.sh.
if [ "$CONFIG_TPM2_TOOLS" = "y" ] && [ ! -f "/tmp/secret/primary.handle" ]; then
	DIE "Unable to seal TOTP secret; no TPM primary handle. Reset the TPM (Options -> TPM/TOTP/HOTP Options -> Reset the TPM in the GUI) then generate a new TOTP secret."
fi

# perform sealing via tpmr.sh. Failures may indicate missing primary handle
# or other TPM state issues. Avoid DO_WITH_DEBUG so interactive prompts
# (TPM owner passphrase on TPM1) are not hidden from the user.
STATUS "Sealing TOTP secret to TPM NVRAM"
if ! tpmr.sh seal "$TOTP_SECRET" "$TPM_NVRAM_SPACE" 0,1,2,3,4,7 "$pcrf" 312 "" "$TPM_PASSPHRASE"; then
	# tpmr.sh already logged details; guide user generically to reset TPM
	DIE "Unable to seal TOTP secret to TPM NVRAM; reset the TPM (Options -> TPM/TOTP/HOTP Options -> Reset the TPM in the GUI) and try again."
fi
STATUS_OK "TOTP secret sealed to TPM successfully"
#Make sure we clear TPM TOTP sealed if we succeed to seal TOTP
shred -n 10 -z -u "$TOTP_SEALED" 2>/dev/null

url="otpauth://totp/$HOST?secret=$secret"

DEBUG "TOTP secret output on screen (both URL and QR code)"
qrenc "$url"

NOTE "TOTP secret for manual input (device without camera): $secret"
secret=""
