Skip to content

Commit 5659d71

Browse files
committed
Use TPM-backed principal enrollment
Create principal signing keys through tpm2-pkcs11 so enrollment stores only the TPM public key while keeping the private key on the principal machine.
1 parent 13d81ee commit 5659d71

2 files changed

Lines changed: 143 additions & 16 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ Gateway startup verifies the authorization registry schema version and fails fas
103103

104104
Shut down cleanly with `Ctrl-C`.
105105

106-
Register a demo principal signing key with:
106+
Register a principal signing key from the TPM owner machine with:
107107

108108
```bash
109109
./examples/register-principal-key.sh org-alice
110110
```
111111

112-
The script creates a P-256 private key under `certs/principals/`, stores the public key in `principal_signing_keys`, and uses the friendly `key_id` (`org-alice`, `org-bob`, etc.) for the registry row.
112+
The script creates or reuses a non-exportable TPM-backed P-256 key through `tpm2_ptool` and PKCS#11, stores only the public key in `principal_signing_keys`, and uses the friendly `key_id` (`org-alice`, `org-bob`, etc.) for the registry row. Run it on the machine that owns the TPM, with `AGENT_GATEWAY_DATABASE_URL` or `DATABASE_URL` pointing at Postgres.
113113

114114
## Authorization Registry
115115

examples/register-principal-key.sh

Lines changed: 141 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,171 @@
22
set -euo pipefail
33

44
usage() {
5-
echo "Usage: $0 KEY_ID [PRIVATE_KEY_PATH] [VALID_DAYS]" >&2
6-
echo "Example: $0 org-alice certs/principals/org-alice.pem 365" >&2
5+
echo "Usage: $0 KEY_ID [VALID_DAYS]" >&2
6+
echo "Example: $0 org-alice 365" >&2
7+
echo >&2
8+
echo "Environment:" >&2
9+
echo " AGENT_GATEWAY_DATABASE_URL or DATABASE_URL must point at Postgres." >&2
10+
echo " TPM2_PKCS11_STORE defaults to \$HOME/.tpm2_pkcs11." >&2
11+
echo " TPM2_PKCS11_MODULE may override libtpm2_pkcs11.so discovery." >&2
12+
echo " AGENT_GATEWAY_TPM_TOKEN_LABEL defaults to agent-gateway." >&2
13+
echo " AGENT_GATEWAY_TPM_USER_PIN and AGENT_GATEWAY_TPM_SO_PIN avoid PIN prompts." >&2
714
}
815

9-
if [[ $# -lt 1 || $# -gt 3 ]]; then
16+
if [[ $# -lt 1 || $# -gt 2 ]]; then
1017
usage
1118
exit 2
1219
fi
1320

1421
KEY_ID="$1"
15-
PRIVATE_KEY="${2:-certs/principals/${KEY_ID}.pem}"
16-
VALID_DAYS="${3:-365}"
22+
VALID_DAYS="${2:-365}"
1723
DATABASE_URL="${AGENT_GATEWAY_DATABASE_URL:-${DATABASE_URL:-}}"
24+
TPM2_PKCS11_STORE="${TPM2_PKCS11_STORE:-$HOME/.tpm2_pkcs11}"
25+
TOKEN_LABEL="${AGENT_GATEWAY_TPM_TOKEN_LABEL:-agent-gateway}"
26+
PRIMARY_ID="${AGENT_GATEWAY_TPM_PRIMARY_ID:-}"
27+
USER_PIN="${AGENT_GATEWAY_TPM_USER_PIN:-}"
28+
SO_PIN="${AGENT_GATEWAY_TPM_SO_PIN:-}"
29+
export TPM2_PKCS11_STORE
1830

1931
if [[ -z "$DATABASE_URL" ]]; then
2032
echo "Set AGENT_GATEWAY_DATABASE_URL or DATABASE_URL" >&2
2133
exit 2
2234
fi
2335

36+
command -v tpm2_ptool >/dev/null || { echo "tpm2_ptool is required" >&2; exit 1; }
37+
command -v pkcs11-tool >/dev/null || { echo "pkcs11-tool is required" >&2; exit 1; }
2438
command -v openssl >/dev/null || { echo "openssl is required" >&2; exit 1; }
2539
command -v psql >/dev/null || { echo "psql is required" >&2; exit 1; }
2640

27-
mkdir -p "$(dirname "$PRIVATE_KEY")"
28-
if [[ ! -f "$PRIVATE_KEY" ]]; then
29-
openssl ecparam -name prime256v1 -genkey -noout -out "$PRIVATE_KEY"
30-
chmod 600 "$PRIVATE_KEY"
31-
fi
32-
3341
tmpdir="$(mktemp -d)"
3442
trap 'rm -rf "$tmpdir"' EXIT
3543

3644
public_der="$tmpdir/public.der"
37-
openssl ec -in "$PRIVATE_KEY" -pubout -outform DER -out "$public_der" 2>/dev/null
45+
public_spki_der="$tmpdir/public-spki.der"
46+
47+
discover_pkcs11_module() {
48+
if [[ -n "${TPM2_PKCS11_MODULE:-}" ]]; then
49+
printf '%s\n' "$TPM2_PKCS11_MODULE"
50+
return
51+
fi
52+
53+
local candidate
54+
for candidate in \
55+
/usr/lib/libtpm2_pkcs11.so.0 \
56+
/usr/lib/libtpm2_pkcs11.so \
57+
/usr/lib/*/libtpm2_pkcs11.so.0 \
58+
/usr/lib/*/libtpm2_pkcs11.so \
59+
/usr/lib/*/pkcs11/libtpm2_pkcs11.so \
60+
/usr/local/lib/pkcs11/libtpm2_pkcs11.so \
61+
/usr/local/lib/libtpm2_pkcs11.so; do
62+
if [[ -e "$candidate" ]]; then
63+
printf '%s\n' "$candidate"
64+
return
65+
fi
66+
done
67+
68+
echo "Set TPM2_PKCS11_MODULE to the path of libtpm2_pkcs11.so" >&2
69+
exit 2
70+
}
71+
72+
prompt_secret() {
73+
local prompt="$1"
74+
local value
75+
read -r -s -p "$prompt" value
76+
echo >&2
77+
printf '%s\n' "$value"
78+
}
3879

3980
hex_file() {
4081
od -An -tx1 -v "$1" | tr -d ' \n'
4182
}
4283

43-
PUBLIC_KEY_HEX="$(hex_file "$public_der")"
84+
run_tpm2_ptool() {
85+
local warnings_filter="ignore::DeprecationWarning"
86+
if [[ -n "${PYTHONWARNINGS:-}" ]]; then
87+
PYTHONWARNINGS="${PYTHONWARNINGS},${warnings_filter}" tpm2_ptool "$@" 2> >(
88+
grep -v \
89+
-e 'CryptographyDeprecationWarning:' \
90+
-e 'from cryptography\.hazmat\.primitives\.ciphers\.' >&2
91+
)
92+
else
93+
PYTHONWARNINGS="$warnings_filter" tpm2_ptool "$@" 2> >(
94+
grep -v \
95+
-e 'CryptographyDeprecationWarning:' \
96+
-e 'from cryptography\.hazmat\.primitives\.ciphers\.' >&2
97+
)
98+
fi
99+
}
100+
101+
run_pkcs11_tool() {
102+
pkcs11-tool "$@" 2> >(
103+
grep -v \
104+
-e '^WARNING:fapi:' \
105+
-e '^ERROR:fapi:.*Fapi_List' \
106+
-e '^ERROR:fapi:.*Entities_List' \
107+
-e '^WARNING: Listing FAPI token objects failed:' \
108+
-e '^Please see https://github.com/tpm2-software/tpm2-pkcs11/blob/.*/docs/FAPI.md' \
109+
-e '^WARNING: Getting tokens from fapi backend failed\.' >&2
110+
)
111+
}
112+
113+
PKCS11_MODULE="$(discover_pkcs11_module)"
114+
115+
if [[ -z "$USER_PIN" ]]; then
116+
USER_PIN="$(prompt_secret "TPM token user PIN: ")"
117+
fi
118+
119+
mkdir -p "$TPM2_PKCS11_STORE"
120+
121+
if ! run_pkcs11_tool --module "$PKCS11_MODULE" --token-label "$TOKEN_LABEL" --list-objects >/dev/null 2>&1; then
122+
if [[ -z "$PRIMARY_ID" ]]; then
123+
init_output="$(run_tpm2_ptool init --path "$TPM2_PKCS11_STORE")"
124+
PRIMARY_ID="$(printf '%s\n' "$init_output" | awk -F': *' '$1 == "id" { print $2; exit }')"
125+
fi
126+
if [[ -z "$PRIMARY_ID" ]]; then
127+
echo "Could not determine tpm2-pkcs11 primary id from tpm2_ptool init output" >&2
128+
exit 1
129+
fi
130+
if [[ -z "$SO_PIN" ]]; then
131+
SO_PIN="$(prompt_secret "New TPM token SO PIN: ")"
132+
fi
133+
run_tpm2_ptool addtoken \
134+
--path "$TPM2_PKCS11_STORE" \
135+
--pid "$PRIMARY_ID" \
136+
--sopin "$SO_PIN" \
137+
--userpin "$USER_PIN" \
138+
--label "$TOKEN_LABEL"
139+
fi
140+
141+
if ! run_pkcs11_tool \
142+
--module "$PKCS11_MODULE" \
143+
--token-label "$TOKEN_LABEL" \
144+
--login \
145+
--pin "$USER_PIN" \
146+
--read-object \
147+
--type pubkey \
148+
--label "$KEY_ID" \
149+
--output-file "$public_der" >/dev/null 2>&1; then
150+
run_tpm2_ptool addkey \
151+
--path "$TPM2_PKCS11_STORE" \
152+
--label "$TOKEN_LABEL" \
153+
--userpin "$USER_PIN" \
154+
--algorithm ecc256 \
155+
--key-label "$KEY_ID"
156+
157+
run_pkcs11_tool \
158+
--module "$PKCS11_MODULE" \
159+
--token-label "$TOKEN_LABEL" \
160+
--login \
161+
--pin "$USER_PIN" \
162+
--read-object \
163+
--type pubkey \
164+
--label "$KEY_ID" \
165+
--output-file "$public_der"
166+
fi
167+
168+
openssl pkey -pubin -inform DER -in "$public_der" -pubout -outform DER -out "$public_spki_der" 2>/dev/null
169+
PUBLIC_KEY_HEX="$(hex_file "$public_spki_der")"
44170

45171
psql "$DATABASE_URL" \
46172
--set=ON_ERROR_STOP=1 \
@@ -70,4 +196,5 @@ ON CONFLICT (key_id) DO UPDATE SET
70196
RETURNING key_id, not_after;
71197
SQL
72198

73-
echo "Private key: $PRIVATE_KEY"
199+
echo "TPM token: $TOKEN_LABEL"
200+
echo "TPM key label: $KEY_ID"

0 commit comments

Comments
 (0)