-
Notifications
You must be signed in to change notification settings - Fork 28
/
tpm2-send
executable file
·281 lines (242 loc) · 7.75 KB
/
tpm2-send
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#!/bin/bash
PROG=${0##*/}
BINDIR=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
TOP=$(dirname "$BINDIR")
if [[ -s $TOP/lib/safeboot/functions.sh ]]; then
# shellcheck disable=SC1090 source=functions.sh
. "$TOP/lib/safeboot/functions.sh"
elif [[ -s $TOP/functions.sh ]]; then
# shellcheck disable=SC1090 source=functions.sh
. "$TOP/functions.sh"
else
echo "Unable to find Safeboot function library" 1>&2
exit 1
fi
set -euo pipefail
shopt -s extglob
# shellcheck disable=SC2209
function usage {
((${1:-1} > 0)) && exec 1>&2
pager=cat
if [[ -t 0 && -t 1 && -t 2 ]]; then
if [[ -z ${PAGER:-} ]] && type less >/dev/null 2>&1; then
pager=less
elif [[ -z ${PAGER:-} ]] && type more >/dev/null 2>&1; then
pager=more
elif [[ -n ${PAGER:-} ]]; then
pager=$PAGER
fi
fi
$pager <<EOF
Usage: $PROG EK-PUB SECRET OUT # Null policy
$PROG EK-PUB SECRET OUT POLICY-CMD [ARGS [\\; ...]]
$PROG -P POLICY EK-PUB SECRET OUT
{$PROG} encrypts a small (up to 32 bytes) {SECRET} file (should
contain an AES key) to a target TPM -identified by {EK-PUB}- with the
caller's optional choice of policy to be enforced by that TPM. The
{EK-PUB} should be a file containing the target's EKpub in
{TPM2B_PUBLIC} format.
Options:
-h This help message.
-M WK|TK Method to use for encryption to TPM (default: WK).
-P POLICY Use the named policy or policyDigest.
-f Overwrite {OUT}.
-x Trace this script.
Policies given as positional arguments should be of the form:
tpm2 policy... args... \\; tpm2 policy args... \\; ...
without any {--session}|{-S} nor {--policy}|{-L} options.
Also, no need to include {tpm2 policycommandcode}, as {$PROG} will add
that automatically.
E.g.:
$ $PROG ./ekpub ./secret ./madecredential \\
tpm2 policypcr -l "sha256:0,1,2,3" -f pcrs
A POLICY can be a digest or an executable.
A POLICY digest would be the SHA-256 policyDigest of a policy.
A POLICY executable would be a program that, if called with no
arguments, outputs a policyDigest.
The two methods of encryption to a TPM are:
- WK Uses {TPM2_MakeCredential()} to encrypt an AES key to
the target's EKpub.
The target uses {TPM2_ActivateCredential()} to decrypt
the AES key.
A well-known key ("WK") is used as the activation object,
and the given policy is associated with it.
This method produces a single file named {OUT}.
- TK Uses {TPM2_Duplicate()} to encrypt an RSA private key to
the target's EKpub, then encrypts an AES key to that
key's public key. That RSA key we refer to as a
"transport key", or TK.
The target uses {TPM2_Import()} to import the TK,
{TPM2_Load()} to load it, and {TPM2_RSA_Decrypt()} to
decrypt the AES key.
A policy, if given, is set on the TK that the TPM will
enforce when {TPM2_RSA_Decrypt()} is called.
This method produces multiple files besides {OUT},
named:
{OUT}.tk.dpriv
{OUT}.tk.seed
EOF
exit "${1:-1}"
}
force=false
method=WK
policy=
policyDigest=
while getopts +:hfxM:P: opt; do
case "$opt" in
M) method=$OPTARG;;
P) policy=$OPTARG;;
h) usage 0;;
f) force=true;;
x) set -vx;;
*) usage;;
esac
done
shift $((OPTIND - 1))
function err {
echo "ERROR: $*" 1>&2
exit 1
}
case "$method" in
WK) command_code=TPM2_CC_ActivateCredential;;
TK) command_code=TPM2_CC_RSA_Decrypt;;
*) err "METHOD must be \"WK\" or \"TK\"";;
esac
if [[ -n $policy ]] && (($# > 3)); then
echo "Error: -P and policy commands are mutually exclusive" 1>&2
exit 1
fi
if [[ -n $policy ]]; then
(($# == 3)) || usage
if ((${#policy} == 64)) &&
[[ ! -f $policy && $policy = +([0-9a-fA-F]) ]]; then
# $policy is a policyDigest
policyDigest=$policy
elif [[ -x $policy ]]; then
# Run the script in $policy to get a policyDigest
policyDigest=$("$policy")
else
err "Given policy is neither a SHA-256 policyDigest nor a policy script"
fi
fi
(($# >= 3)) || usage
ekpub_file=$1
secret_file=$2
out_file=$3
shift 3
[[ -f ${ekpub_file:-} ]] || usage
[[ -f ${secret_file:-} ]] || usage
[[ -f ${out_file:-} ]] && $force && rm -f "${out_file:-}"
[[ -f ${out_file:-} ]] && err "output file ($out_file) exists"
# Make a temp dir and remove it when we exit:
d=
trap 'rm -rf "$d"' EXIT
d=$(mktemp -d)
# Compute a well-known activation object's name for use in
# TPM2_MakeCredential(), binding a given policy into it.
#
# This version uses a TPM via {tpm2 loadexternal}.
function wkname_tpm {
local attrs='sign'
local has_policy=
tpm2 flushcontext --transient-object
tpm2 flushcontext --loaded-session
tpm2 flushcontext --saved-session 1>&2
# Load
if [[ -n $policyDigest ]]; then
tpm2 startauthsession --session "${d}/session.ctx"
printf '%s' "$policyDigest" | xxd -p -r > "${d}/policy"
echo "policyDigest: $(xxd -p -c 100 < "${d}/policy")" 1>&2
attrs='adminwithpolicy|sign'
has_policy=true
elif (($# > 0)); then
make_policyDigest "$command_code" "$@" 1>&2
attrs='adminwithpolicy|sign'
has_policy=true
# Flush again, but this time not saved sessions
tpm2 flushcontext --transient-object 1>&2
tpm2 flushcontext --loaded-session 1>&2
echo "policyDigest: $(xxd -p -c 100 < "${d}/policy")" 1>&2
fi
# Load the WK
wkpriv > "${d}/wkpriv.pem"
tpm2 loadexternal \
--hierarchy n \
--key-algorithm ecc \
--private "${d}/wkpriv.pem" \
${has_policy:+ --policy "${d}/policy"} \
--attributes "$attrs" \
--key-context "${d}/wk.ctx" \
| grep ^name: | cut -d' ' -f2 \
|| die "unable to load the WK into a TPM for computing its name"
}
# Return the cryptographic name of a well-known activation object that
# binds the given policy.
function wkname {
# Use hard-coded WKnames for known policies. These are computed for
# the WKpriv above.
#
# TODO: Hardcode more policies here.
if [[ -z $policyDigest ]] && (($#)); then
wkname_tpm "$@"
return 0
fi
case "X$policyDigest" in
X7fdad037a921f7eec4f97c08722692028e96888f0b970dc7b3bb6a9c97e8f988)
# pcr11
echo 000b20a6cc44c93ad206196c65028f9a8bf2590de0b8f89bca9e968f09f4e616dba6;;
X)
# null policy
echo 000bc76d1462d32d5e6051d0aa121edfa5ed66b8e7f3632ce3c5a172b1ebd8aabc40;;
*)
# If we have a TPM, use it and compute the WKname on the fly
[[ -n ${TPM2TOOLS_TCTI:-} ]] \
|| die "No TPM is available to compute WKname for the requested policy $policyDigest $*"
wkname_tpm "$@";;
esac
}
case "$method" in
WK) info "Computing WKname"
wkname=$(wkname "$@") \
|| die "unable to compute the MakeCredential activation object's cryptographic name"
info "Encrypting to EKpub using TPM2_MakeCredential"
tpm2 makecredential \
--tcti "none" \
--encryption-key "${ekpub_file}" \
--name "$wkname" \
--secret "${secret_file}" \
--credential-blob "$out_file" \
|| die "unable to MakeCredential";;
TK) info "Generating TK"
openssl genrsa -out "${d}/tk-priv.pem" \
|| die "unable to create TK private key"
openssl rsa \
-pubout \
-in "${d}/tk-priv.pem" \
-out "${d}/tk.pem" \
|| die "unable to create TK public key"
args=()
if (($# > 0)); then
make_policyDigest "$command_code" "$@" 1>&2
args=("--policy=${d}/policy")
fi
info "Exporting TK to EKpub"
tpm2 duplicate \
--tcti none \
--parent-public="$ekpub_file" \
--wrapper-algorithm=rsa \
"${args[@]}" \
--private-key="${d}/tk-priv.pem" \
--public="${out_file}.tk.pub" \
--private="${out_file}.tk.dpriv" \
--encrypted-seed="${out_file}.tk.seed" \
|| die "$0: unable to duplicate TK into TPM for EK"
info "Encrypting to TK"
openssl rsautl \
-encrypt \
-pubin \
-inkey "${d}/tk.pem" \
-in "$secret_file" \
-out "${out_file}" \
|| die "$0: unable to encrypt to TK" ;;
esac