mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-20 05:28:08 +00:00
initrd/bin/tpmr: replay PCR values from event log instead of assumming their values
Signed-off-by: Krystian Hebel <krystian.hebel@3mdeb.com>
This commit is contained in:
parent
38dfa73f7c
commit
d1a18f1f83
@ -93,8 +93,8 @@ tpmr pcrread 0 "$pcrf"
|
||||
tpmr pcrread -a 1 "$pcrf"
|
||||
tpmr pcrread -a 2 "$pcrf"
|
||||
tpmr pcrread -a 3 "$pcrf"
|
||||
# Note that PCR 4 needs to be set with the "normal-boot" path value, which is 0.
|
||||
dd if=/dev/zero bs="$(tpmr pcrsize)" count=1 status=none >> "$pcrf"
|
||||
# Note that PCR 4 needs to be set with the "normal-boot" path value, read it from event log.
|
||||
tpmr calcfuturepcr 4 >> "$pcrf"
|
||||
if [ "$CONFIG_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_verification ]; then
|
||||
DEBUG "Sealing TPM disk unlock key with PCR5 involvement (additional kernel modules are loaded per board config)..."
|
||||
# Here, we take pcr 5 into consideration if modules are expected to be measured+loaded
|
||||
@ -102,11 +102,11 @@ if [ "$CONFIG_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_v
|
||||
else
|
||||
DEBUG "Sealing TPM disk unlock key with PCR5=0 (NO additional kernel modules are loaded per board config)..."
|
||||
#no kernel modules are expected to be measured+loaded
|
||||
dd if=/dev/zero bs="$(tpmr pcrsize)" count=1 status=none >> "$pcrf"
|
||||
tpmr calcfuturepcr 5 >> "$pcrf"
|
||||
fi
|
||||
# Precompute the value for pcr 6
|
||||
DEBUG "Precomputing TPM future value for PCR6 sealing/unsealing of TPM disk unlock key..."
|
||||
tpmr calcfuturepcr -a "/tmp/luksDump.txt" "$pcrf"
|
||||
tpmr calcfuturepcr 6 "/tmp/luksDump.txt" >> "$pcrf"
|
||||
# We take into consideration user files in cbfs
|
||||
tpmr pcrread -a 7 "$pcrf"
|
||||
|
||||
|
@ -30,17 +30,17 @@ dd \
|
||||
|
||||
secret="`base32 < $TOTP_SECRET`"
|
||||
pcrf="/tmp/secret/pcrf.bin"
|
||||
DEBUG "Sealing TOTP with actual state of PCR0-4)"
|
||||
DEBUG "Sealing TOTP with actual state of PCR0-3"
|
||||
tpmr pcrread 0 "$pcrf"
|
||||
tpmr pcrread -a 1 "$pcrf"
|
||||
tpmr pcrread -a 2 "$pcrf"
|
||||
tpmr pcrread -a 3 "$pcrf"
|
||||
DEBUG "Sealing TOTP with actual state of PCR4 (Going to recovery shell extends PCR4)"
|
||||
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.
|
||||
#We expect the PCR4 to be in the right state at unattended unseal operation
|
||||
tpmr pcrread -a 4 "$pcrf"
|
||||
# Read from event log to catch both cases, even when called from recovery shell.
|
||||
tpmr 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
|
||||
|
163
initrd/bin/tpmr
163
initrd/bin/tpmr
@ -86,45 +86,134 @@ tpm1_pcrread() {
|
||||
DO_WITH_DEBUG tpm pcrread -ix "$index" | hex2bin >>"$file"
|
||||
}
|
||||
|
||||
# usage: tpmr calcfuturepcr [-a] <input_file> <output_file>
|
||||
# Uses the scratch PCR to calculate a future PCR value (TPM2 23, TPM1 16). The
|
||||
# data in input file are hashed into a PCR, and the PCR value is placed in
|
||||
# output_file.
|
||||
# -a: Append to output_file. Default is to overwrite
|
||||
tpm2_calcfuturepcr() {
|
||||
TRACE "Under /bin/tpmr:tpm2_calcfuturepcr"
|
||||
if [ "$1" = "-a" ]; then
|
||||
APPEND=y
|
||||
shift
|
||||
fi
|
||||
|
||||
input_file="$1"
|
||||
output_file="$2"
|
||||
|
||||
if [ -z "$APPEND" ]; then
|
||||
true >"$output_file"
|
||||
fi
|
||||
|
||||
tpm2 pcrreset -Q 23
|
||||
DO_WITH_DEBUG tpmr extend -ix 23 -if "$input_file"
|
||||
DO_WITH_DEBUG tpm2 pcrread -Q -o >(cat >>"$output_file") sha256:23
|
||||
tpm2 pcrreset -Q 23
|
||||
# is_hash - Check if a value is a valid hash of a given type
|
||||
# usage: is_hash <alg> <value>
|
||||
is_hash() {
|
||||
# Must only contain 0-9a-fA-F
|
||||
if [ "$(echo -n "$2" | tr -d '0-9a-fA-F' | wc -c)" -ne 0 ]; then return 1; fi
|
||||
# SHA-1 hashes are 40 chars
|
||||
if [ "$1" = "sha1" ] && [ "${#2}" -eq 40 ]; then return 0; fi
|
||||
# SHA-256 hashes are 64 chars
|
||||
if [ "$1" = "sha256" ] && [ "${#2}" -eq 64 ]; then return 0; fi
|
||||
return 1
|
||||
}
|
||||
tpm1_calcfuturepcr() {
|
||||
TRACE "Under /bin/tpmr:tpm1_calcfuturepcr"
|
||||
if [ "$1" = "-a" ]; then
|
||||
APPEND=y
|
||||
|
||||
# extend_pcr_state - extend a PCR state value with more hashes or raw data (which is hashed)
|
||||
# usage:
|
||||
# extend_pcr_state <alg> <initial_state> <files/hashes...>
|
||||
# alg - either 'sha1' or 'sha256' to specify algorithm
|
||||
# initial_state - a hash value setting the initial state
|
||||
# files/hashes... - any number of files or hashes, state is extended once for each item
|
||||
extend_pcr_state() {
|
||||
local alg="$1"
|
||||
local state="$2"
|
||||
local next extend
|
||||
shift 2
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
next="$1"
|
||||
shift
|
||||
if is_hash "$alg" "$next"; then
|
||||
extend="$next"
|
||||
else
|
||||
extend="$("${alg}sum" <"$next" | cut -d' ' -f1)"
|
||||
fi
|
||||
state="$(echo "$state$extend" | hex2bin | "${alg}sum" | cut -d' ' -f1)"
|
||||
done
|
||||
echo "$state"
|
||||
}
|
||||
|
||||
# There are 3 (and a half) possible formats of event log, each of them requires
|
||||
# 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'
|
||||
TPM2 log:
|
||||
Specification: 2.00
|
||||
Platform class: PC Client
|
||||
TPM2 log entry 1:
|
||||
PCR: 2
|
||||
Event type: Action
|
||||
Digests:
|
||||
SHA256: de73053377e1ae5ba5d2b637a4f5bfaeb410137722f11ef135e7a1be524e3092
|
||||
SHA1: 27c4f1fa214480c8626397a15981ef3a9323717f
|
||||
Event data: FMAP: FMAP
|
||||
EOF
|
||||
# 2) TPM1.2 log (aka TCPA), digest is always SHA1:
|
||||
: << 'EOF'
|
||||
TCPA log:
|
||||
Specification: 1.21
|
||||
Platform class: PC Client
|
||||
TCPA log entry 1:
|
||||
PCR: 2
|
||||
Event type: Action
|
||||
Digest: 27c4f1fa214480c8626397a15981ef3a9323717f
|
||||
Event data: FMAP: FMAP
|
||||
EOF
|
||||
# 3) coreboot-specific format:
|
||||
# 3.5) older versions printed 'coreboot TCPA log', even though it isn't TCPA
|
||||
: << 'EOF'
|
||||
coreboot TPM log:
|
||||
|
||||
PCR-2 27c4f1fa214480c8626397a15981ef3a9323717f SHA1 [FMAP: FMAP]
|
||||
EOF
|
||||
|
||||
# awk script to handle all of the above. Note this gets squashed to one line so
|
||||
# semicolons are required.
|
||||
AWK_PROG='
|
||||
BEGIN {
|
||||
getline;
|
||||
hash_regex="([a-fA-F0-9]{40,})";
|
||||
if ($0 == "TPM2 log:") {
|
||||
RS="\n[^[:space:]]";
|
||||
pcr="PCR: " pcr;
|
||||
alg=toupper(alg) ": " hash_regex;
|
||||
} else if ($0 == "TCPA log:") {
|
||||
RS="\n[^[:space:]]";
|
||||
pcr="PCR: " pcr;
|
||||
alg="Digest: " hash_regex;
|
||||
} else if ($0 ~ /^coreboot (TCPA|TPM) log:$/) {
|
||||
pcr="PCR-" pcr;
|
||||
alg=hash_regex " " toupper(alg) " ";
|
||||
} else {
|
||||
print "Unknown TPM event log format:", $0 > "/dev/stderr";
|
||||
exit -1;
|
||||
}
|
||||
}
|
||||
$0 ~ pcr {
|
||||
match($0, alg);
|
||||
print gensub(alg, "\\1", "g", substr($0, RSTART, RLENGTH));
|
||||
}
|
||||
'
|
||||
|
||||
# usage: replay_pcr <alg> <pcr_num> [ <input_file>|<input_hash> ... ]
|
||||
# Replays PCR value from CBMEM event log. Note that this contains only the
|
||||
# measurements performed by firmware, without those performed by Heads (USB
|
||||
# modules, LUKS header etc). First argument is PCR number, followed by optional
|
||||
# hashes and/or files extended to given PCR after firmware. Resulting PCR value
|
||||
# is returned in binary form.
|
||||
replay_pcr() {
|
||||
TRACE "Under /bin/tpmr:replay_pcr"
|
||||
if [ -z "$2" ] ; then
|
||||
>&2 echo "No PCR number passed"
|
||||
return
|
||||
fi
|
||||
|
||||
input_file="$1"
|
||||
output_file="$2"
|
||||
|
||||
if [ -z "$APPEND" ]; then
|
||||
true >"$output_file"
|
||||
if [ "$2" -ge 8 ] ; then
|
||||
>&2 echo "Illegal PCR number ($2)"
|
||||
return
|
||||
fi
|
||||
|
||||
DO_WITH_DEBUG tpm calcfuturepcr -ix 16 -if "$input_file" | hex2bin >>"$output_file"
|
||||
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
|
||||
# SHA-256 hashes are 64 chars
|
||||
if [ "$alg" = "sha256" ] ; then alg_digits=64; fi
|
||||
shift 2
|
||||
extend_pcr_state $alg $(printf "%.${alg_digits}d" 0) \
|
||||
$(echo "$log" | awk -v alg=$alg -v pcr=$pcr -f <(echo $AWK_PROG)) \
|
||||
$@ | hex2bin
|
||||
}
|
||||
|
||||
tpm2_extend() {
|
||||
@ -575,7 +664,7 @@ if [ "$CONFIG_TPM2_TOOLS" != "y" ]; then
|
||||
pcrsize)
|
||||
echo "$PCR_SIZE";;
|
||||
calcfuturepcr)
|
||||
shift; tpm1_calcfuturepcr "$@";;
|
||||
shift; replay_pcr "sha1" "$@";;
|
||||
seal)
|
||||
shift; tpm1_seal "$@";;
|
||||
startsession)
|
||||
@ -606,7 +695,7 @@ case "$subcmd" in
|
||||
pcrsize)
|
||||
echo "$PCR_SIZE";;
|
||||
calcfuturepcr)
|
||||
tpm2_calcfuturepcr "$@";;
|
||||
replay_pcr "sha256" "$@";;
|
||||
extend)
|
||||
tpm2_extend "$@";;
|
||||
counter_read)
|
||||
|
Loading…
Reference in New Issue
Block a user