seal-totp/tpmr: differenciate die messages to show which between tpm1_seal/tpm2_seal or check_tpm_counter fails to seal as first step to possible refactor

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
This commit is contained in:
Thierry Laurion 2023-11-03 10:15:37 -04:00
parent 51caab8ea4
commit e2985d386e
No known key found for this signature in database
GPG Key ID: E7B4A71658E36A93
2 changed files with 195 additions and 156 deletions

View File

@ -52,7 +52,7 @@ tpmr seal "$TOTP_SECRET" "$TPM_NVRAM_SPACE" 0,1,2,3,4,7 "$pcrf" 312 "" "$TPM_PAS
{
shred -n 10 -z -u /tmp/secret/tpm_owner_password 2>/dev/null
:
die "Unable to write sealed secret to NVRAM"
die "Unable to write sealed secret to NVRAM from seal-totp"
}
#Make sure we clear TPM TOTP sealed if we succeed to seal TOTP
shred -n 10 -z -u "$TOTP_SEALED" 2>/dev/null

View File

@ -115,7 +115,7 @@ extend_pcr_state() {
shift
if is_hash "$alg" "$next"; then
extend="$next"
else
else
extend="$("${alg}sum" <"$next" | cut -d' ' -f1)"
fi
state="$(echo "$state$extend" | hex2bin | "${alg}sum" | cut -d' ' -f1)"
@ -127,7 +127,7 @@ extend_pcr_state() {
# different arguments for grep. Those formats are shown below as heredocs to
# keep all the data, including whitespaces:
# 1) TPM2 log, which can hold multiple hash algorithms at once:
: << 'EOF'
: <<'EOF'
TPM2 log:
Specification: 2.00
Platform class: PC Client
@ -140,7 +140,7 @@ TPM2 log entry 1:
Event data: FMAP: FMAP
EOF
# 2) TPM1.2 log (aka TCPA), digest is always SHA1:
: << 'EOF'
: <<'EOF'
TCPA log:
Specification: 1.21
Platform class: PC Client
@ -152,7 +152,7 @@ TCPA log entry 1:
EOF
# 3) coreboot-specific format:
# 3.5) older versions printed 'coreboot TCPA log', even though it isn't TCPA
: << 'EOF'
: <<'EOF'
coreboot TPM log:
PCR-2 27c4f1fa214480c8626397a15981ef3a9323717f SHA1 [FMAP: FMAP]
@ -194,25 +194,25 @@ $0 ~ pcr {
# is returned in binary form.
replay_pcr() {
TRACE "Under /bin/tpmr:replay_pcr"
if [ -z "$2" ] ; then
>&2 echo "No PCR number passed"
if [ -z "$2" ]; then
echo >&2 "No PCR number passed"
return
fi
if [ "$2" -ge 8 ] ; then
>&2 echo "Illegal PCR number ($2)"
if [ "$2" -ge 8 ]; then
echo >&2 "Illegal PCR number ($2)"
return
fi
local log=`cbmem -L`
local log=$(cbmem -L)
local alg="$1"
local pcr="$2"
local alg_digits=0
# SHA-1 hashes are 40 chars
if [ "$alg" = "sha1" ] ; then alg_digits=40; fi
if [ "$alg" = "sha1" ]; then alg_digits=40; fi
# SHA-256 hashes are 64 chars
if [ "$alg" = "sha256" ] ; then alg_digits=64; fi
if [ "$alg" = "sha256" ]; then alg_digits=64; fi
shift 2
replayed_pcr=$(extend_pcr_state $alg $(printf "%.${alg_digits}d" 0) \
$(echo "$log" | awk -v alg=$alg -v pcr=$pcr -f <(echo $AWK_PROG)) $@)
$(echo "$log" | awk -v alg=$alg -v pcr=$pcr -f <(echo $AWK_PROG)) $@)
echo $replayed_pcr | hex2bin
DEBUG "Replayed cbmem -L clean boot state of PCR=$pcr ALG=$alg : $replayed_pcr"
# To manually introspect current PCR values:
@ -232,17 +232,21 @@ tpm2_extend() {
TRACE "Under /bin/tpmr:tpm2_extend"
while true; do
case "$1" in
-ix)
index="$2"
shift 2;;
-ic)
hash="$(echo -n "$2"|sha256sum|cut -d' ' -f1)"
shift 2;;
-if)
hash="$(sha256sum "$2"|cut -d' ' -f1)"
shift 2;;
*)
break;;
-ix)
index="$2"
shift 2
;;
-ic)
hash="$(echo -n "$2" | sha256sum | cut -d' ' -f1)"
shift 2
;;
-if)
hash="$(sha256sum "$2" | cut -d' ' -f1)"
shift 2
;;
*)
break
;;
esac
done
tpm2 pcrextend "$index:sha256=$hash"
@ -253,57 +257,67 @@ tpm2_counter_read() {
TRACE "Under /bin/tpmr:tpm2_counter_read"
while true; do
case "$1" in
-ix)
index="$2"
shift 2;;
*)
break;;
-ix)
index="$2"
shift 2
;;
*)
break
;;
esac
done
echo "$index: `tpm2 nvread 0x$index | xxd -pc8`"
echo "$index: $(tpm2 nvread 0x$index | xxd -pc8)"
}
tpm2_counter_inc() {
TRACE "Under /bin/tpmr:tpm2_counter_inc"
while true; do
case "$1" in
-ix)
index="$2"
shift 2;;
-pwdc)
pwd="$2"
shift 2;;
*)
break;;
-ix)
index="$2"
shift 2
;;
-pwdc)
pwd="$2"
shift 2
;;
*)
break
;;
esac
done
tpm2 nvincrement "0x$index" > /dev/console
echo "$index: `tpm2 nvread 0x$index | xxd -pc8`"
tpm2 nvincrement "0x$index" >/dev/console
echo "$index: $(tpm2 nvread 0x$index | xxd -pc8)"
}
tpm2_counter_cre() {
TRACE "Under /bin/tpmr:tpm2_counter_cre"
while true; do
case "$1" in
-pwdo)
pwdo="$2"
shift 2;;
-pwdof)
pwdo="file:$2"
shift 2;;
-pwdc)
pwd="$2"
shift 2;;
-la)
label="$2"
shift 2;;
*)
break;;
-pwdo)
pwdo="$2"
shift 2
;;
-pwdof)
pwdo="file:$2"
shift 2
;;
-pwdc)
pwd="$2"
shift 2
;;
-la)
label="$2"
shift 2
;;
*)
break
;;
esac
done
rand_index="1`dd if=/dev/urandom bs=1 count=3 | xxd -pc3`"
rand_index="1$(dd if=/dev/urandom bs=1 count=3 | xxd -pc3)"
tpm2 nvdefine -C o -s 8 -a "ownerread|authread|authwrite|nt=1" \
-P "$(tpm2_password_hex "$pwdo")" "0x$rand_index" > /dev/console
-P "$(tpm2_password_hex "$pwdo")" "0x$rand_index" >/dev/console
echo "$rand_index: (valid after an increment)"
}
@ -311,16 +325,16 @@ tpm2_startsession() {
TRACE "Under /bin/tpmr:tpm2_startsession"
mkdir -p "$SECRET_DIR"
tpm2 flushcontext -Q \
--transient-object \
|| die "tpm2_flushcontext: unable to flush transient handles"
--transient-object ||
die "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q \
--loaded-session \
|| die "tpm2_flushcontext: unable to flush sessions"
--loaded-session ||
die "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q \
--saved-session \
|| die "tpm2_flushcontext: unable to flush saved session"
--saved-session ||
die "tpm2_flushcontext: unable to flush saved session"
tpm2 readpublic -Q -c "$PRIMARY_HANDLE" -t "$PRIMARY_HANDLE_FILE"
tpm2 startauthsession -Q -c "$PRIMARY_HANDLE_FILE" --hmac-session -S "$ENC_SESSION_FILE"
tpm2 startauthsession -Q -c "$PRIMARY_HANDLE_FILE" --hmac-session -S "$DEC_SESSION_FILE"
@ -354,27 +368,27 @@ cleanup_shred() {
# tpm2_destroy: Destroy a sealed file in the TPM. The mechanism differs by
# TPM version - TPM2 evicts the file object, so it no longer exists.
tpm2_destroy() {
index="$1" # Index of the sealed file
size="$2" # Size of zeroes to overwrite for TPM1 (unused in TPM2)
index="$1" # Index of the sealed file
size="$2" # Size of zeroes to overwrite for TPM1 (unused in TPM2)
# Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc.
handle="$(printf "0x81%6s" "$index" | tr ' ' 0)"
# remove possible data occupying this handle
tpm2 evictcontrol -Q -C p -c "$handle" 2>/dev/null \
|| die "Unable to evict secret"
tpm2 evictcontrol -Q -C p -c "$handle" 2>/dev/null ||
die "Unable to evict secret"
}
# tpm1_destroy: Destroy a sealed file in the TPM. The mechanism differs by
# TPM version - TPM1 overwrites the file with zeroes, since this can be done
# without authorization. (Deletion requires authorization.)
tpm1_destroy() {
index="$1" # Index of the sealed file
size="$2" # Size of zeroes to overwrite for TPM1
index="$1" # Index of the sealed file
size="$2" # Size of zeroes to overwrite for TPM1
dd if=/dev/zero bs="$size" count=1 of=/tmp/wipe-totp-zero
tpm nv_writevalue -in "$index" -if /tmp/wipe-totp-zero \
|| die "Unable to wipe sealed secret"
tpm nv_writevalue -in "$index" -if /tmp/wipe-totp-zero ||
die "Unable to wipe sealed secret"
}
# tpm2_seal: Seal a file against PCR values and, optionally, a password.
@ -388,13 +402,13 @@ tpm2_seal() {
index="$2"
pcrl="$3" #0,1,2,3,4,5,6,7 (does not include algorithm prefix)
pcrf="$4"
sealed_size="$5" # Not used for TPM2
pass="$6" # May be empty to seal with no password
tpm_password="$7" # Owner password - will prompt if needed and not empty
sealed_size="$5" # Not used for TPM2
pass="$6" # May be empty to seal with no password
tpm_password="$7" # Owner password - will prompt if needed and not empty
# TPM Owner Password is always needed for TPM2.
mkdir -p "$SECRET_DIR"
bname="`basename $file`"
bname="$(basename $file)"
# Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc.
handle="$(printf "0x81%6s" "$index" | tr ' ' 0)"
@ -448,7 +462,8 @@ tpm2_seal() {
-c "$handle" 2>/dev/null || true
DO_WITH_DEBUG --mask-position 6 \
tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_owner_password")" \
-c "$SECRET_DIR/$bname.seal.ctx" "$handle"
-c "$SECRET_DIR/$bname.seal.ctx" "$handle" ||
die "Unable to write sealed secret to NVRAM from tpm2_seal"
}
tpm1_seal() {
TRACE "Under /bin/tpmr:tpm1_seal"
@ -457,8 +472,8 @@ tpm1_seal() {
pcrl="$3" #0,1,2,3,4,5,6,7 (does not include algorithm prefix)
pcrf="$4"
sealed_size="$5"
pass="$6" # May be empty to seal with no password
tpm_password="$7" # Owner password - will prompt if needed and not empty
pass="$6" # May be empty to seal with no password
tpm_password="$7" # Owner password - will prompt if needed and not empty
sealed_file="$SECRET_DIR/tpm1_seal_sealed.bin"
at_exit cleanup_shred "$sealed_file"
@ -477,8 +492,8 @@ tpm1_seal() {
# Read each PCR_SIZE block from the file and pass as hex
POLICY_ARGS+=(-ix "$pcr"
"$(dd if="$pcrf" skip="$pcr_file_index" bs="$PCR_SIZE" count=1 status=none | xxd -p | tr -d ' ')"
)
pcr_file_index=$((pcr_file_index+1))
)
pcr_file_index=$((pcr_file_index + 1))
done
tpm sealfile2 \
@ -494,18 +509,17 @@ tpm1_seal() {
#
# The permissions are 0 since there is nothing special
# about the sealed file
tpm physicalpresence -s \
|| warn "Unable to assert physical presence"
tpm physicalpresence -s ||
warn "Unable to assert physical presence"
prompt_tpm_owner_password
tpm nv_definespace -in "$index" -sz "$sealed_size" \
-pwdo "$tpm_password" -per 0 \
|| warn "Unable to define NVRAM space; trying anyway"
-pwdo "$tpm_password" -per 0 ||
warn "Unable to define NVRAM space; trying anyway"
tpm nv_writevalue -in "$index" -if "$sealed_file" \
|| die "Unable to write sealed secret to NVRAM"
tpm nv_writevalue -in "$index" -if "$sealed_file" ||
die "Unable to write sealed secret to NVRAM from tpm1_seal"
fi
}
@ -555,7 +569,7 @@ tpm2_unseal() {
fi
tpm2 unseal -Q -c "$handle" -p "session:$POLICY_SESSION$UNSEAL_PASS_SUFFIX" \
-S "$ENC_SESSION_FILE" > "$file"
-S "$ENC_SESSION_FILE" >"$file"
}
tpm1_unseal() {
TRACE "Under /bin/tpmr:tpm1_unseal"
@ -577,8 +591,8 @@ tpm1_unseal() {
tpm nv_readvalue \
-in "$index" \
-sz "$sealed_size" \
-of "$sealed_file" \
|| die "Unable to read sealed file from TPM NVRAM"
-of "$sealed_file" ||
die "Unable to read sealed file from TPM NVRAM"
PASS_ARGS=()
if [ "$pass" ]; then
@ -598,7 +612,7 @@ tpm2_reset() {
mkdir -p "$SECRET_DIR"
# output TPM Owner Password to a file to be reused in this boot session until recovery shell/reboot
DEBUG "Caching TPM Owner Password to $SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" > "$SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" >"$SECRET_DIR/tpm_owner_password"
tpm2 clear -c platform || warn "Unable to clear TPM on platform hierarchy"
tpm2 changeauth -c owner "$(tpm2_password_hex "$tpm_owner_password")"
tpm2 changeauth -c endorsement "$(tpm2_password_hex "$tpm_owner_password")"
@ -647,7 +661,7 @@ tpm1_reset() {
mkdir -p "$SECRET_DIR"
# output tpm_owner_password to a file to be reused in this boot session until recovery shell/reboot
DEBUG "Caching TPM Owner Password to $SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" > "$SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" >"$SECRET_DIR/tpm_owner_password"
# Make sure the TPM is ready to be reset
tpm physicalpresence -s
tpm physicalenable
@ -667,20 +681,20 @@ tpm2_kexec_finalize() {
TRACE "Under /bin/tpmr:tpm2_kexec_finalize"
# Flush sessions and transient objects
tpm2 flushcontext -Q --transient-object \
|| warn "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q --loaded-session \
|| warn "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q --saved-session \
|| warn "tpm2_flushcontext: unable to flush saved session"
tpm2 flushcontext -Q --transient-object ||
warn "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q --loaded-session ||
warn "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q --saved-session ||
warn "tpm2_flushcontext: unable to flush saved session"
# Add a random passphrase to platform hierarchy to prevent TPM2 from
# being cleared in the OS.
# This passphrase is only effective before the next boot.
echo "Locking TPM2 platform hierarchy..."
randpass=$(dd if=/dev/urandom bs=4 count=1 status=none | xxd -p)
tpm2 changeauth -c platform "$randpass" \
|| warn "Failed to lock platform hierarchy of TPM2"
tpm2 changeauth -c platform "$randpass" ||
warn "Failed to lock platform hierarchy of TPM2"
}
tpm2_shutdown() {
@ -700,72 +714,97 @@ fi
# TPM1 - most commands forward directly to tpm, but some are still wrapped for
# consistency with tpm2.
if [ "$CONFIG_TPM2_TOOLS" != "y" ]; then
PCR_SIZE=20 # TPM1 PCRs are always SHA-1
PCR_SIZE=20 # TPM1 PCRs are always SHA-1
subcmd="$1"
# Don't shift yet, for most commands we will just forward to tpm.
case "$subcmd" in
pcrread)
shift; tpm1_pcrread "$@";;
pcrsize)
echo "$PCR_SIZE";;
calcfuturepcr)
shift; replay_pcr "sha1" "$@";;
destroy)
shift; tpm1_destroy "$@";;
seal)
shift; tpm1_seal "$@";;
startsession)
;; # Nothing on TPM1.
unseal)
shift; tpm1_unseal "$@";;
reset)
shift; tpm1_reset "$@";;
kexec_finalize)
;; # Nothing on TPM1.
shutdown)
;; # Nothing on TPM1.
*)
DEBUG "Direct translation from tpmr to tpm1 call"
DO_WITH_DEBUG exec tpm "$@"
;;
pcrread)
shift
tpm1_pcrread "$@"
;;
pcrsize)
echo "$PCR_SIZE"
;;
calcfuturepcr)
shift
replay_pcr "sha1" "$@"
;;
destroy)
shift
tpm1_destroy "$@"
;;
seal)
shift
tpm1_seal "$@"
;;
startsession) ;; # Nothing on TPM1.
unseal)
shift
tpm1_unseal "$@"
;;
reset)
shift
tpm1_reset "$@"
;;
kexec_finalize) ;; # Nothing on TPM1.
shutdown) ;; # Nothing on TPM1.
*)
DEBUG "Direct translation from tpmr to tpm1 call"
DO_WITH_DEBUG exec tpm "$@"
;;
esac
exit 0
fi
# TPM2 - all commands implemented as wrappers around tpm2
PCR_SIZE=32 # We use the SHA-256 PCRs
PCR_SIZE=32 # We use the SHA-256 PCRs
subcmd="$1"
shift 1
case "$subcmd" in
pcrread)
tpm2_pcrread "$@";;
pcrsize)
echo "$PCR_SIZE";;
calcfuturepcr)
replay_pcr "sha256" "$@";;
extend)
tpm2_extend "$@";;
counter_read)
tpm2_counter_read "$@";;
counter_increment)
tpm2_counter_inc "$@";;
counter_create)
tpm2_counter_cre "$@";;
destroy)
tpm2_destroy "$@";;
seal)
tpm2_seal "$@";;
startsession)
tpm2_startsession "$@";;
unseal)
tpm2_unseal "$@";;
reset)
tpm2_reset "$@";;
kexec_finalize)
tpm2_kexec_finalize "$@";;
shutdown)
tpm2_shutdown "$@";;
*)
echo "Command $subcmd not wrapped!"
exit 1
pcrread)
tpm2_pcrread "$@"
;;
pcrsize)
echo "$PCR_SIZE"
;;
calcfuturepcr)
replay_pcr "sha256" "$@"
;;
extend)
tpm2_extend "$@"
;;
counter_read)
tpm2_counter_read "$@"
;;
counter_increment)
tpm2_counter_inc "$@"
;;
counter_create)
tpm2_counter_cre "$@"
;;
destroy)
tpm2_destroy "$@"
;;
seal)
tpm2_seal "$@"
;;
startsession)
tpm2_startsession "$@"
;;
unseal)
tpm2_unseal "$@"
;;
reset)
tpm2_reset "$@"
;;
kexec_finalize)
tpm2_kexec_finalize "$@"
;;
shutdown)
tpm2_shutdown "$@"
;;
*)
echo "Command $subcmd not wrapped!"
exit 1
;;
esac