rust/pvimg: Add 'tools/check_hostkeydoc' script

This is done in preparation for the Rust port of genprotimg.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Marc Hartmayer
2024-11-26 17:29:19 +00:00
committed by Jan Höppner
parent f99719a695
commit f524b0b8dc
2 changed files with 307 additions and 0 deletions

View File

@@ -44,6 +44,8 @@ pvimg-bootloaders:
$(MAKE) -C pvimg/boot/
.PHONY: pvimg-bootloaders
PVIMG_PKGDATADIR := $(TOOLS_DATADIR)/pvimg
export PVIMG_PKGDATADIR
BUILD_TARGETS += $(PV_BUILD_TARGETS) pvimg-bootloaders
INSTALL_TARGETS += pvimg-bootloaders
@@ -83,6 +85,8 @@ all: $(BUILD_TARGETS)
install: $(INSTALL_TARGETS)
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvattest/tools/pvextract-hdr $(DESTDIR)$(USRBINDIR)
$(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvimg/tools/check_hostkeydoc $(DESTDIR)$(PVIMG_PKGDATADIR)
$(MAKE) -C pvimg/boot install
print-rust-targets:

303
rust/pvimg/tools/check_hostkeydoc Executable file
View File

@@ -0,0 +1,303 @@
#!/bin/sh
#
# check_hostkeydoc - Verify an IBM Secure Execution host key document
#
# Sample script to verify that a host key document is genuine by
# verifying the issuer, the validity date and the signature.
# Optionally verify the full trust chain using a CA certificate.
#
# Sample invocation:
#
# ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl
#
# Copyright IBM Corp. 2020
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
# Allocate temporary files
ISSUER_PUBKEY_FILE=$(mktemp)
SIGNATURE_FILE=$(mktemp)
BODY_FILE=$(mktemp)
ISSUER_DN_FILE=$(mktemp)
SUBJECT_DN_FILE=$(mktemp)
DEF_ISSUER_ARMONK_DN_FILE=$(mktemp)
DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp)
CANONICAL_ISSUER_DN_FILE=$(mktemp)
CRL_SERIAL_FILE=$(mktemp)
# Cleanup on exit
cleanup()
{
rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \
"$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \
"$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE"
}
trap cleanup EXIT
# Enhanced error checking for bash
if [ -n "${BASH}" ]; then
# shellcheck disable=SC3040
set -o posix
# shellcheck disable=SC3040
set -o pipefail
# shellcheck disable=SC3040
set -o nounset
fi
set -e
# Usage
usage()
{
cat <<-EOF
Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
Verify an IBM Secure Execution host key document against
a signing key.
Options:
-d disable default issuer check of host-key-doc
-c CA-cert trusted CA certificate
-r CRL list of revoked host-key-docs
Note that in order to have the full trust chain verified
it is necessary to provide the issuing CA's certificate.
The default issuer check may be disabled if a non-default
signing key certificate needs to be verified against the
CA certificate.
EOF
}
check_verify_chain()
{
# Verify certificate chain in case a CA certificate file/bundle
# was specified on the command line.
if [ -z "$2" ]; then
cat >&2 <<-EOF
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
No CA certificate specified! Skipping trust chain verification.
Make sure that '$1' is a valid certificate.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EOF
else
openssl verify -crl_download -crl_check "$2" &&
openssl verify -crl_download -crl_check -untrusted "$2" "$1" ||
exit 1
fi
}
extract_pubkey()
{
openssl x509 -in "$1" -pubkey -noout >"$2"
}
extract_signature()
{
# Assuming that the last field is the signature
SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
extract_body()
{
# Assuming that the first field is the full cert body
SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
verify_signature()
{
# Assuming that the signature algorithm is SHA512 with RSA
openssl sha512 -verify "$1" -signature "$2" "$3"
}
canonical_dn()
{
OBJTYPE=$1
OBJ=$2
DNTYPE=$3
OUTPUT=$4
openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline |
LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT"
}
default_issuer_armonk()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Armonk
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
default_issuer_pougkeepsie()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Poughkeepsie
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
# As organizationalUnitName can have an arbitrary prefix but must
# end with "Key Signing Service" let's normalize the OU name by
# stripping off the prefix
verify_default_issuer()
{
default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE"
default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE"
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
"$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE"
if ! {
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" ||
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE"
} >/dev/null 2>&1; then
echo Incorrect default issuer >&2 && exit 1
fi
}
verify_issuer_files()
{
if [ "$1" -eq 1 ]; then
verify_default_issuer
fi
}
cert_time()
{
DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
crl_time()
{
DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
verify_dates()
{
START="$1"
END="$2"
MSG="${3:-Certificate}"
NOW=$(date +%s)
if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then
echo "${MSG} dates are OK"
else
echo "${MSG} date verification failed" >&2 && exit 1
fi
}
crl_serials()
{
openssl crl -in "$1" -text -noout |
grep "Serial Number" >"$CRL_SERIAL_FILE"
}
check_serial()
{
CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2)
grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE"
}
check_file()
{
[ -e "$1" ] ||
(echo "File '$1' not found" >&2 && exit 1)
}
# check args
CRL_FILE=
CA_FILE=
CHECK_DEFAULT_ISSUER=1
while getopts 'dr:c:h' opt; do
case $opt in
d) CHECK_DEFAULT_ISSUER=0 ;;
r) CRL_FILE=$OPTARG ;;
c) CA_FILE=$OPTARG ;;
h)
usage "$0"
exit 0
;;
?)
usage "$0"
exit 1
;;
esac
done
shift "$((OPTIND - 1))"
if [ $# -ne 2 ]; then
usage "$0" >&2
exit 1
fi
HKD_FILE=$1
HKSK_FILE=$2
# Check whether all specified files exist
check_file "$HKD_FILE"
check_file "$HKSK_FILE"
# CA and CRL are optional arguments
[ -n "$CA_FILE" ] && check_file "$CA_FILE"
[ -n "$CRL_FILE" ] && check_file "$CRL_FILE"
# Check trust chain
check_verify_chain "$HKSK_FILE" "$CA_FILE"
# Verify host key document signature
printf "Checking host key document signature: "
extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" &&
extract_signature "$HKD_FILE" "$SIGNATURE_FILE" &&
extract_body "$HKD_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
# Verify the issuer
canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
# Verify dates
verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)"
# Check CRL if specified
if [ -n "$CRL_FILE" ]; then
printf "Checking CRL signature: "
extract_signature "$CRL_FILE" "$SIGNATURE_FILE" &&
extract_body "$CRL_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
printf "CRL "
canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL'
crl_serials "$CRL_FILE"
check_serial "$HKD_FILE" &&
echo "Certificate is revoked, do not use it anymore!" >&2 &&
exit 1
fi
# We made it
echo All checks requested for \'"$HKD_FILE"\' were successful