Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TPM Key Support #74

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6e5e91b
Add stub of TPMv2 support
dwmw2 Jun 12, 2023
3610922
Flesh out unimplemented TPMv2Signer a little more
dwmw2 Jun 14, 2023
a17714e
Basic shell of TPM, working at least for EC
dwmw2 Jun 14, 2023
eda60b6
Add RSA support
dwmw2 Jun 15, 2023
72ead0c
Add TPM support to docs
dwmw2 Jun 15, 2023
77a97d5
Add tests for TPM
dwmw2 Jul 3, 2023
5d35347
Allow $TPM_DEVICE to set TPM device path
dwmw2 Jul 4, 2023
908569c
Add swtpm testing
dwmw2 Jul 4, 2023
2944557
Test cert + key both in one PEM file
dwmw2 Jul 4, 2023
42afaef
Merge in dwmw2:tpm
13ajay Jun 12, 2024
e785ffd
Merge branch 'dwmw2-tpm' into pkcs11
13ajay Jun 12, 2024
7767d1d
Support password-protected TPM signing keys
13ajay Jun 26, 2024
aca9860
Add testing for RSA TPM keys that have the Sign capability
13ajay Jun 26, 2024
5cec079
Code formatting and clean up
13ajay Jun 26, 2024
38e0412
Add further documentation around TPM keys
13ajay Jun 26, 2024
cceb33d
Add TPM capability check before signing
13ajay Jun 27, 2024
085de24
Add parent key password support
13ajay Jun 28, 2024
2b1ea32
Add tests to verify parent key password support
13ajay Aug 11, 2024
8a09473
Fix builds on Windows
13ajay Sep 6, 2024
dfbcf65
Introduce flags to convey intent when not using TPM key passwords
13ajay Sep 19, 2024
70904cc
Add some more tests for TPMv2Signer
13ajay Sep 19, 2024
7bdd7a9
Accept TPM key handles
13ajay Oct 17, 2024
0694dfc
Switch Makefile to using Intel tools to generate TPM fixtures
13ajay Oct 18, 2024
54968db
Determine authorization requirement based on CLI flags or key file
13ajay Oct 19, 2024
d6c3740
Improve TPM key guidance
13ajay Oct 19, 2024
a17522f
Fix 'scripts' section of README
13ajay Oct 23, 2024
574140c
Modify TPM key file password indication behavior
13ajay Oct 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 221 additions & 30 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build/bin/aws_signing_helper:
go build -buildmode=pie -ldflags "-X 'github.com/aws/rolesanywhere-credential-helper/cmd.Version=${VERSION}' -linkmode=external -w -s" -trimpath -o build/bin/aws_signing_helper main.go

.PHONY: clean
clean:
clean: test-clean
rm -rf build

# Setting up SoftHSM for PKCS#11 tests.
Expand All @@ -25,9 +25,156 @@ ECCERTS := $(foreach digest, sha1 sha256 sha384 sha512, $(patsubst %-key.pem, %-
RSACERTS := $(foreach digest, md5 sha1 sha256 sha384 sha512, $(patsubst %-key.pem, %-$(digest)-cert.pem, $(RSAKEYS)))
PKCS12CERTS := $(patsubst %-cert.pem, %.p12, $(RSACERTS) $(ECCERTS))

# It's hard to do a file-based rule for the contents of the SoftHSM token.
# Software TPM. For generating keys/certs, we run the swtpm in TCP mode,
# because that's what the tools and the OpenSSL ENGINE require. Each of
# the rules which might need the swtpm will ensure that it's running by
# invoking $(START_SWTPM_TCP). The 'test-certs:; rule will then *stop* it
# by running $(STOP_SWTPM_TCP), after all the certs and keys have been
# created.
#
# For the actual test, we need it to run in UNIX socket mode, since
# *that* is all that go-tpm can cope with. So we start it in that mode
# in the 'test:' (or 'test-tpm-signer:') recipe(s), and stop it again
# afterwards.
SWTPM_STATEDIR := $(curdir)/tst/swtpm
SWTPM_CTRLSOCK := $(curdir)/tst/swtpm-ctrl
SWTPM_SERVSOCK := $(curdir)/tst/swtpm-serv
SWTPM := swtpm socket --tpm2 --tpmstate dir=$(SWTPM_STATEDIR)
TABRMD_NAME := com.intel.tss2.Tabrmd2321

# Annoyingly, while we only support UNIX socket, the ENGINE only supports TCP.
SWTPM_UNIX := --server type=unixio,path=$(SWTPM_SERVSOCK) --ctrl type=unixio,path=$(SWTPM_CTRLSOCK)
SWTPM_NET := --server type=tcp,port=2321 --ctrl type=tcp,port=2322

SWTPM_PREFIX := TPM2TOOLS_TCTI=tabrmd:bus_name="$(TABRMD_NAME)",bus_type=session \
TPM2OPENSSL_TCTI=tabrmd:bus_name="$(TABRMD_NAME)",bus_type=session

# Check that the swtpm is running for TCP connections. This isn't a normal
# phony rule because we don't want it running unless there's actually some
# work to be done over the TCP socket (creating keys, certs, etc.).
START_SWTPM_TCP := \
if ! swtpm_ioctl --tcp 127.0.0.1:2322 -g >/dev/null 2>/dev/null; then \
mkdir -p $(SWTPM_STATEDIR); \
$(SWTPM) $(SWTPM_NET) --flags not-need-init,startup-clear -d; \
( tpm2-abrmd --session --dbus-name="$(TABRMD_NAME)" --tcti "swtpm:host=localhost,port=2321" & ); \
$(SWTPM_PREFIX) tpm2_dictionarylockout -s -t 0; \
fi
STOP_SWTPM_TCP := swtpm_ioctl --tcp 127.0.0.1:2322 -s && kill $$(pgrep tpm2-abrmd)

# This one is used for the actual test run
START_SWTPM_UNIX := \
if ! swtpm_ioctl --unix $(SWTPM_CTRLSOCK) -g >/dev/null 2>/dev/null; then \
$(SWTPM) $(SWTPM_UNIX) --flags not-need-init,startup-clear -d; \
fi
STOP_SWTPM_UNIX := swtpm_ioctl --unix $(SWTPM_CTRLSOCK) -s

$(certsdir)/tpm-sw-rsa-key.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) ./create_tpm2_key.sh -r $@

$(certsdir)/tpm-sw-rsa-key-with-pw.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) ./create_tpm2_key.sh -r -k 1234 $@

$(certsdir)/tpm-sw-ec-prime256-key.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) ./create_tpm2_key.sh -e prime256v1 $@

$(certsdir)/tpm-sw-ec-prime256-key-with-pw.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) ./create_tpm2_key.sh -e prime256v1 -k 1234 $@

$(certsdir)/tpm-sw-ec-secp384r1-key.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) ./create_tpm2_key.sh -e secp384r1 $@

$(certsdir)/tpm-sw-ec-secp384r1-key-with-pw.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) ./create_tpm2_key.sh -e secp384r1 -k 1234 $@

$(certsdir)/tpm-sw-loaded-81000101-ec-secp384r1-key.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) tpm2_createprimary -c parent.ctx
$(SWTPM_PREFIX) tpm2_create -C parent.ctx -u child.pub -r child.priv
$(SWTPM_PREFIX) tpm2_load -C parent.ctx -u child.pub -r child.priv -c child.ctx
$(SWTPM_PREFIX) tpm2_evictcontrol -c child.ctx 0x81000101
rm parent.ctx child.pub child.priv child.ctx

$(certsdir)/tpm-sw-loaded-81000102-ec-secp384r1-key-with-pw.pem:
$(START_SWTPM_TCP)
$(SWTPM_PREFIX) tpm2_createprimary -c parent.ctx
$(SWTPM_PREFIX) tpm2_create -C parent.ctx -u child.pub -r child.priv -p 1234
$(SWTPM_PREFIX) tpm2_load -C parent.ctx -u child.pub -r child.priv -c child.ctx
$(SWTPM_PREFIX) tpm2_evictcontrol -c child.ctx 0x81000102
rm parent.ctx child.pub child.priv child.ctx

# Create a persistent key at 0x81000001 in the owner hierarchy, if it
# doesn't already exist. And a PEM key with that as its parent.
$(certsdir)/tpm-sw-ec-81000001-key.pem:
$(START_SWTPM_TCP)
if ! $(SWTPM_PREFIX) tpm2_readpublic -c 0x81000001; then \
$(SWTPM_PREFIX) tpm2_createprimary -G rsa -c parent.ctx && \
$(SWTPM_PREFIX) tpm2_evictcontrol -c parent.ctx 0x81000001; \
fi
$(SWTPM_PREFIX) openssl genpkey -provider tpm2 -algorithm EC -pkeyopt group:prime256v1 -pkeyopt parent:0x81000001 -out $@

$(certsdir)/tpm-sw-ec-81000001-key-with-pw.pem:
$(START_SWTPM_TCP)
if ! $(SWTPM_PREFIX) tpm2_readpublic -c 0x81000001; then \
$(SWTPM_PREFIX) tpm2_createprimary -G rsa -c parent.ctx && \
$(SWTPM_PREFIX) tpm2_evictcontrol -c parent.ctx 0x81000001; \
fi
$(SWTPM_PREFIX) openssl genpkey -provider tpm2 -algorithm EC -pkeyopt group:prime256v1 -pkeyopt parent:0x81000001 -pkeyopt user-auth:1234 -out $@

# Create RSA keys with the Sign capability
$(certsdir)/tpm-sw-rsa-81000001-sign-key.pem:
if ! $(SWTPM_PREFIX) tpm2_readpublic -c 0x81000001; then \
$(SWTPM_PREFIX) tpm2_createprimary -G rsa -c parent.ctx && \
$(SWTPM_PREFIX) tpm2_evictcontrol -c parent.ctx 0x81000001; \
fi
$(SWTPM_PREFIX) openssl genpkey -provider tpm2 -algorithm RSA -pkeyopt parent:0x81000001 -out $@

$(certsdir)/tpm-sw-rsa-81000001-sign-key-with-pw.pem:
if ! $(SWTPM_PREFIX) tpm2_readpublic -c 0x81000001; then \
$(SWTPM_PREFIX) tpm2_createprimary -G rsa -c parent.ctx && \
$(SWTPM_PREFIX) tpm2_evictcontrol -c parent.ctx 0x81000001; \
fi
$(SWTPM_PREFIX) openssl genpkey -provider tpm2 -algorithm RSA -pkeyopt parent:0x81000001 -pkeyopt user-auth:1234 -out $@

SWTPM_LOADED_KEYS_WO_PW := $(certsdir)/tpm-sw-loaded-81000101-ec-secp384r1-key.pem
SWTPM_LOADED_KEYS_W_PW := $(certsdir)/tpm-sw-loaded-81000102-ec-secp384r1-key-with-pw.pem
SWTPMKEYS_WO_PW_WO_SIGN_CAP := $(certsdir)/tpm-sw-rsa-key.pem
SWTPMKEYS_WO_PW := $(certsdir)/tpm-sw-ec-secp384r1-key.pem $(certsdir)/tpm-sw-ec-prime256-key.pem $(certsdir)/tpm-sw-rsa-81000001-sign-key.pem
SWTPMKEYS_W_PW := $(patsubst %.pem, %-with-pw.pem, $(SWTPMKEYS_WO_PW)) $(certsdir)/tpm-sw-ec-81000001-key.pem $(certsdir)/tpm-sw-ec-81000001-key-with-pw.pem $(certsdir)/tpm-sw-rsa-81000001-sign-key-with-pw.pem
SWTPMKEYS := $(SWTPMKEYS_WO_PW) $(SWTPMKEYS_W_PW) $(SWTPMKEYS_WO_PW_WO_SIGN_CAP)
SWTPM_LOADED_KEY_CERTS := $(foreach digest, sha1 sha256 sha384 sha512, $(patsubst %-key.pem, %-$(digest)-cert.pem, $(SWTPM_LOADED_KEYS_WO_PW)))
SWTPMCERTS := $(foreach digest, sha1 sha256 sha384 sha512, $(patsubst %-key.pem, %-$(digest)-cert.pem, $(SWTPMKEYS_WO_PW)))

HWTPMKEYS_WO_PW := $(certsdir)/tpm-hw-rsa-key.pem $(certsdir)/tpm-hw-ec-key.pem $(certsdir)/tpm-hw-ec-81000001-key.pem
HWTPMKEYS_W_PW := $(patsubst %.pem, %-with-pw.pem, $(HWTPMKEYS_WO_PW))
HWTPMKEYS := $(HWTPMKEYS_WO_PW) $(HWTPMKEYS_W_PW)
HWTPMCERTS := $(foreach digest, sha1 sha256 sha384 sha512, $(patsubst %-key.pem, %-$(digest)-cert.pem, $(HWTPMKEYS_WO_PW)))

# User can test on hardware TPM with `make TPM_DEVICE=/dev/tpmrm0 test`
ifeq ($(TPM_DEVICE),)
TPM_DEVICE := $(SWTPM_SERVSOCK)
TPMKEYS := $(SWTPMKEYS)
TPMCERTS := $(SWTPMCERTS)
TPMLOADEDKEY_CERTS := $(SWTPM_LOADED_KEY_CERTS)
START_SWTPM := $(START_SWTPM_UNIX)
STOP_SWTPM := $(STOP_SWTPM_UNIX)
else
TPMKEYS := $(HWTPMKEYS)
TPMCERTS := $(HWTPMCERTS)
START_SWTPM := true
STOP_SWTPM := true
endif

export TPM_DEVICE

# It's hard to ao a file-based rule for the contents of the SoftHSM token.
# So just populate it as a side-effect of creating the softhsm2.conf file.
tst/softhsm2.conf: tst/softhsm2.conf.template $(PKCS8KEYS) $(RSACERTS) $(ECCERTS) tst/certs/rsa-2048-2-sha256-cert.pem
tst/softhsm2.conf: tst/softhsm2.conf.template $(PKCS8KEYS) $(RSACERTS) $(ECCERTS)
rm -rf tst/softhsm/*
sed 's|@top_srcdir@|${curdir}|g' $< > $@.tmp
$(SHM2_UTIL) --show-slots
Expand Down Expand Up @@ -59,31 +206,40 @@ tst/softhsm2.conf: tst/softhsm2.conf.template $(PKCS8KEYS) $(RSACERTS) $(ECCERTS

.PHONY: test
test: test-certs tst/softhsm2.conf
SOFTHSM2_CONF=$(curdir)/tst/softhsm2.conf go test -v ./...

%-md5-cert.pem: %-key.pem
SUBJ=$$(echo "$@" | sed -r 's|.*/([^/]+)-cert.pem|\1|'); \
openssl req -x509 -new -key $< -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -md5
%-sha1-cert.pem: %-key.pem
SUBJ=$$(echo "$@" | sed -r 's|.*/([^/]+)-cert.pem|\1|'); \
openssl req -x509 -new -key $< -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -sha1
%-sha256-cert.pem: %-key.pem
SUBJ=$$(echo "$@" | sed -r 's|.*/([^/]+)-cert.pem|\1|'); \
openssl req -x509 -new -key $< -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -sha256
%-2-sha256-cert.pem: %-key.pem
SUBJ=$$(echo "$@" | sed -r 's|.*/([^/]+)-cert.pem|\1|'); \
openssl req -x509 -new -key $< -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -sha256
%-sha384-cert.pem: %-key.pem
SUBJ=$$(echo "$@" | sed -r 's|.*/([^/]+)-cert.pem|\1|'); \
openssl req -x509 -new -key $< -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -sha384
%-sha512-cert.pem: %-key.pem
SUBJ=$$(echo "$@" | sed -r 's|.*/([^/]+)-cert.pem|\1|'); \
openssl req -x509 -new -key $< -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -sha512
$(START_SWTPM)
SOFTHSM2_CONF=$(curdir)/tst/softhsm2.conf go test -v ./... || :
$(STOP_SWTPM)

TPMCOMBOS := $(patsubst %-cert.pem, %-combo.pem, $(TPMCERTS))

.PHONY: test-tpm-signer
test-tpm-signer: $(certsdir)/cert-bundle.pem $(TPMKEYS) $(TPMCERTS) $(TPMLOADEDKEY_CERTS) $(TPMCOMBOS)
$(STOP_SWTPM_TCP) 2>/dev/null || :
$(START_SWTPM)
go test ./... -run "TPM"
$(STOP_SWTPM)

define CERT_RECIPE
@SUBJ=$$(echo "$@" | sed 's^\(.*/\)\?\([^/]*\)-cert.pem^\2^'); \
[ "$${SUBJ#tpm-}" != "$${SUBJ}" ] && ENG="-provider tpm2 -provider default -propquery '?provider=tpm2'"; \
if [ "$${SUBJ#tpm-sw-}" != "$${SUBJ}" ]; then $(START_SWTPM_TCP); TPM_PREFIX="$(SWTPM_PREFIX)"; fi; \
if echo $< | grep -q "loaded"; then KEY=handle:0x$(word 4, $(subst -, , $<)); else KEY=$<; fi; \
echo $${TPM_PREFIX} openssl req -x509 -new $${ENG} -key $${KEY} -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -$${SUBJ##*-}; \
eval $${TPM_PREFIX} openssl req -x509 -new $${ENG} -key $${KEY} -out $@ -days 10000 -subj "/CN=roles-anywhere-$${SUBJ}" -$${SUBJ##*-};
endef

%-md5-cert.pem: %-key.pem; $(CERT_RECIPE)
%-sha256-cert.pem: %-key.pem; $(CERT_RECIPE)
%-sha1-cert.pem: %-key.pem; $(CERT_RECIPE)
%-sha384-cert.pem: %-key.pem; $(CERT_RECIPE)
%-sha512-cert.pem: %-key.pem; $(CERT_RECIPE)

%-combo.pem: %-cert.pem
KEY=$$(echo "$@" | sed 's/-[^-]*-combo.pem/-key.pem/'); \
cat $${KEY} $< > $@.tmp && mv $@.tmp $@

# Go PKCS#12 only supports SHA1 and 3DES!!
%.p12: %-pass.p12
echo Creating $@...
ls -l $<
%.p12: %-cert.pem
KEY=$$(echo "$@" | sed 's/-[^-]*\.p12/-key.pem/'); \
CERT=$$(echo "$@" | sed 's/.p12/-cert.pem/'); \
openssl pkcs12 -export -passout pass: -macalg SHA1 \
Expand All @@ -105,6 +261,32 @@ test: test-certs tst/softhsm2.conf
%-pkcs8.pem: %.pem
openssl pkcs8 -topk8 -inform PEM -outform PEM -in $< -out $@ -nocrypt

$(certsdir)/tpm-hw-rsa-key.pem:
./create_tpm2_key.sh -r $@

$(certsdir)/tpm-hw-rsa-key-with-pw.pem:
./create_tpm2_key.sh -r -k 1234 $@

$(certsdir)/tpm-hw-ec-key.pem:
./create_tpm2_key.sh -e prime256v1 $@

$(certsdir)/tpm-hw-ec-key-with-pw.pem:
./create_tpm2_key.sh -e prime256v1 -k 1234 $@

$(certsdir)/tpm-hw-ec-81000001-key.pem:
if ! tpm2_readpublic -c 0x81000001; then \
tpm2_createprimary -G rsa -c parent.ctx && \
tpm2_evictcontrol -c parent.ctx 0x81000001; \
fi
openssl genpkey -provider tpm2 -algorithm EC -pkeyopt group:prime256v1 -pkeyopt parent:0x81000001 -out $@

$(certsdir)/tpm-hw-ec-81000001-key.pem:
if ! tpm2_readpublic -c 0x81000001; then \
tpm2_createprimary -G rsa -c parent.ctx && \
tpm2_evictcontrol -c parent.ctx 0x81000001; \
fi
openssl genpkey -provider tpm2 -algorithm EC -pkeyopt group:prime256v1 -pkeyopt parent:0x81000001 -pkeyopt user-auth:1234 -out $@

$(RSAKEYS):
KEYLEN=$$(echo "$@" | sed 's/.*rsa-\([0-9]*\)-key.pem/\1/'); \
openssl genrsa -out $@ $${KEYLEN}
Expand All @@ -122,16 +304,25 @@ $(certsdir)/cert-bundle-with-comments.pem: $(RSACERTS) $(ECCERTS)
echo "Comment in bundle\n" >> $@; \
done

KEYS := $(RSAKEYS) $(ECKEYS) $(TPMKEYS) $(PKCS8KEYS)
CERTS := $(RSACERTS) $(ECCERTS) $(TPMCERTS)
COMBOS := $(patsubst %-cert.pem, %-combo.pem, $(CERTS))

.PHONY: test-certs
test-certs: $(PKCS8KEYS) $(RSAKEYS) $(ECKEYS) $(RSACERTS) $(ECCERTS) $(PKCS12CERTS) $(certsdir)/cert-bundle.pem $(certsdir)/cert-bundle-with-comments.pem tst/softhsm2.conf
test-certs: $(KEYS) $(CERTS) $(COMBOS) $(PKCS12CERTS) $(certsdir)/cert-bundle.pem $(certsdir)/cert-bundle-with-comments.pem tst/softhsm2.conf
$(STOP_SWTPM_TCP) 2>/dev/null || :

.PHONY: test-clean
test-clean:
rm -f $(RSAKEYS) $(ECKEYS)
rm -f $(RSAKEYS) $(ECKEYS) $(HWTPMKEYS)
rm -f $(PKCS8KEYS)
rm -f $(RSACERTS) $(ECCERTS)
rm -f $(PKCS12CERTS)
rm -f $(RSACERTS) $(ECCERTS) $(HWTPMCERTS)
rm -f $(PKCS12CERTS) $(COMBOS)
rm -f $(certsdir)/cert-bundle.pem
rm -f $(certsdir)/cert-with-comments.pem
rm -f tst/softhsm2.conf
rm -rf tst/softhsm/*
$(STOP_SWTPM_TCP) || :
$(STOP_SWTPM_UNIX) || :
rm -rf $(SWTPMKEYS) $(SWTPMCERTS) $(SWTPM_TMPKEYS) $(SWTPM_STATEDIR)

Loading