Skip to content

Commit 8980e46

Browse files
author
Dmitrii Kuvaiskii
committed
Add swtpm example
Signed-off-by: Dmitrii Kuvaiskii <[email protected]>
1 parent 2e41bcd commit 8980e46

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

swtpm/Makefile

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine)
2+
3+
ifeq ($(DEBUG),1)
4+
GRAMINE_LOG_LEVEL = all
5+
else
6+
GRAMINE_LOG_LEVEL = error
7+
endif
8+
9+
ifeq ($(SGX),1)
10+
GRAMINE_ENCRYPTION_KEY = "_sgx_mrenclave"
11+
else
12+
GRAMINE_ENCRYPTION_KEY = "direct"
13+
endif
14+
15+
.PHONY: all
16+
all: swtpm.manifest
17+
ifeq ($(SGX),1)
18+
all: swtpm.manifest.sgx swtpm.sig
19+
endif
20+
21+
swtpm.manifest: swtpm.manifest.template
22+
gramine-manifest \
23+
-Dlog_level=$(GRAMINE_LOG_LEVEL) \
24+
-Dencryption_key=$(GRAMINE_ENCRYPTION_KEY) \
25+
-Darch_libdir=$(ARCH_LIBDIR) \
26+
-Dentrypoint=$(realpath $(shell sh -c "command -v swtpm")) \
27+
$< $@
28+
29+
# gramine-sgx-sign generates both a .sig file and a .manifest.sgx file. This is somewhat
30+
# hard to express properly in Make. The simple solution would be to use
31+
# "Rules with Grouped Targets" (`&:`), however make on Ubuntu <= 20.04 doesn't support it.
32+
#
33+
# Simply using a normal rule with "two targets" is equivalent to creating separate rules
34+
# for each of the targets, and when using `make -j`, this might cause two instances
35+
# of gramine-sgx-sign to get launched simultaneously, potentially breaking the build.
36+
#
37+
# As a workaround, we use a dummy intermediate target, and mark both files as depending on it, to
38+
# get the dependency graph we want. We mark this dummy target as .INTERMEDIATE, which means
39+
# that make will consider the source tree up-to-date even if the sgx_sign file doesn't exist,
40+
# as long as the other dependencies check out. This is in contrast to .PHONY, which would
41+
# be rebuilt on every invocation of make.
42+
swtpm.sig swtpm.manifest.sgx: sgx_sign
43+
@:
44+
45+
.INTERMEDIATE: sgx_sign
46+
sgx_sign: swtpm.manifest
47+
gramine-sgx-sign \
48+
--manifest $< \
49+
--output $<.sgx
50+
51+
ifeq ($(SGX),)
52+
GRAMINE = gramine-direct
53+
else
54+
GRAMINE = gramine-sgx
55+
endif
56+
57+
.PHONY: clean
58+
clean:
59+
$(RM) *.token *.sig *.manifest.sgx *.manifest myvtpm2/.lock myvtpm2/*.permall
60+
61+
.PHONY: distclean
62+
distclean: clean

swtpm/README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# swtpm
2+
3+
This directory contains a Makefile and a manifest template for running swtpm.
4+
See https://github.com/stefanberger/swtpm/.
5+
6+
**NOTE**: Currently works only with PR https://github.com/gramineproject/gramine/pull/1210.
7+
See also https://github.com/stefanberger/swtpm/issues/792.
8+
9+
# Installing
10+
11+
1. Install `libtpms` like this:
12+
https://github.com/stefanberger/libtpms/wiki#build-a-package-on-ubuntu
13+
- Version used: `git checkout v0.9.6`
14+
15+
2. Install `swtpm` like this: https://github.com/stefanberger/swtpm/wiki#compile-on-ubuntu-2104
16+
- Don't install `libtpms-dev` since we've done it already in step 1
17+
- Version used: `git checkout v0.8.0`
18+
19+
Now swtpm tools are installed. We run only `swtpm` executable with Gramine.
20+
21+
# Configuration of `swtpm`
22+
23+
`swtpm` executable can be run in several modes. We hard-code the following configuration
24+
(command-line options) to run with Gramine:
25+
```sh
26+
$ swtpm socket --tpm2 --tpmstate dir=/myvtpm2 --seccomp action=none \
27+
--server type=tcp,port=2321,disconnect --ctrl type=tcp,port=2320 \
28+
--flags not-need-init,startup-clear
29+
```
30+
31+
This configuration means:
32+
- run `swtpm` in TPM2 mode,
33+
- save all TPM state under `/myvtpm2/` dir (encrypted under Gramine with SGX),
34+
- don't use seccomp (Gramine doesn't support it, and it's not needed in Gramine env anyway),
35+
- listen for client connections on TCP/IP port 2321 (in contrast to CUSE or chardev),
36+
- create a control channel on TCP/IP port 2320,
37+
- additional flags for the initial state of TPM.
38+
39+
For more information, see `man swtpm`.
40+
41+
# Building
42+
43+
- `make clean; make` for Gramine without SGX (`gramine-direct`).
44+
- `make clean; make SGX=1` for Gramine with SGX (`gramine-sgx`).
45+
46+
You can add `DEBUG=1` for verbose Gramine logs.
47+
48+
Notice that `gramine-direct` uses a dummy encryption key for TPM files, hard-coded in the manifest.
49+
Whereas `gramine-sgx` uses the MRENCLAVE-based sealing encryption key for TPM files (and is
50+
therefore secure). To make sure the correct key is used, we require a `make clean` step. For details
51+
on how the key is chosen, see Makefile and manifest template.
52+
53+
# Quick tests of swtpm with Gramine
54+
55+
# 1. Self-test
56+
57+
The test idea is taken from https://github.com/stefanberger/swtpm/wiki/Useful-scripts-for-TPM,
58+
Section "Trigger a self-test on a TPM 2 listening on command port 2321 with the disconnect flag".
59+
60+
```sh
61+
# swtpm server in one window
62+
gramine-sgx swtpm
63+
64+
# client script in another window
65+
bash -c "exec 100<>/dev/tcp/localhost/2321; \
66+
echo -en '\x80\x01\x00\x00\x00\x0b\x00\x00\x01\x43\x01' >&100; \
67+
od -tx1 <&100"
68+
69+
## output must be like this:
70+
## 0000000 80 01 00 00 00 0a 00 00 00 00
71+
```
72+
73+
# 2. Hashing in PCR 17
74+
75+
The test idea is taken from the unit test:
76+
https://github.com/stefanberger/swtpm/blob/346b3d62/tests/_test_tpm2_hashing.
77+
78+
```sh
79+
# swtpm server in one window
80+
gramine-sgx swtpm
81+
82+
# client scripts in another window
83+
84+
## 1 step: init TPM to known state
85+
swtpm_ioctl --tcp localhost:2320 -i
86+
87+
## 2 step: startup TPM2
88+
bash -c "exec 100<>/dev/tcp/localhost/2321; \
89+
echo -en '\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00' >&100; \
90+
od -tx1 <&100"
91+
92+
## output must be like this:
93+
## 0000000 80 01 00 00 00 0a 00 00 00 00
94+
95+
## 3 step: ask TPM to hash string "1234" in PCR 17
96+
swtpm_ioctl --tcp localhost:2320 -h 1234
97+
98+
## 4 step: read PCR 17
99+
bash -c "exec 100<>/dev/tcp/localhost/2321; \
100+
echo -en '\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b\x03\x00\x00\x02' >&100; \
101+
od -tx1 <&100"
102+
103+
## output must be like this:
104+
## 0000000 80 01 00 00 00 3e 00 00 00 00 00 00 00 2c 00 00
105+
## 0000020 00 01 00 0b 03 00 00 02 00 00 00 01 00 20 fc a5
106+
## 0000040 d6 49 bf b0 c9 22 fd 33 0f 79 b2 00 43 28 9d af
107+
## 0000060 d6 0d 01 a4 c4 37 3c f2 8a db 56 c9 b4 54
108+
109+
## 5 step: check TPM Established flag (must be 1)
110+
swtpm_ioctl --tcp localhost:2320 -e
111+
112+
## 6 step: shutdown TPM
113+
swtpm_ioctl --tcp localhost:2320 -s
114+
```

swtpm/myvtpm2/.dummy

Whitespace-only changes.

swtpm/swtpm.manifest.template

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# swtpm (TPM emulator) manifest file example
2+
3+
loader.entrypoint = "file:{{ gramine.libos }}"
4+
libos.entrypoint = "{{ entrypoint }}"
5+
loader.log_level = "{{ log_level }}"
6+
7+
loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}"
8+
9+
loader.argv = [
10+
"swtpm", "socket", "--tpm2",
11+
"--tpmstate", "dir=/myvtpm2",
12+
"--seccomp", "action=none",
13+
"--server", "type=tcp,port=2321,disconnect",
14+
"--ctrl", "type=tcp,port=2320",
15+
"--flags", "not-need-init,startup-clear",
16+
]
17+
18+
sys.enable_sigterm_injection = true
19+
sys.enable_extra_runtime_domain_names_conf = true
20+
21+
fs.mounts = [
22+
{ path = "/lib", uri = "file:{{ gramine.runtimedir() }}" },
23+
{ path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" },
24+
{ path = "/usr/{{ arch_libdir }}", uri = "file:/usr/{{ arch_libdir }}" },
25+
{ path = "/usr/lib/swtpm/", uri = "file:/usr/lib/swtpm/" },
26+
27+
{ path = "{{ entrypoint }}", uri = "file:{{ entrypoint }}" },
28+
{ type = "encrypted", path = "/myvtpm2/", uri = "file:myvtpm2/", key_name = "{{ encryption_key }}" },
29+
]
30+
31+
{% if encryption_key == "direct" %}
32+
fs.insecure__keys.direct = "ffeeddccbbaa99887766554433221100"
33+
{% endif %}
34+
35+
sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}
36+
sgx.enclave_size = "1G"
37+
38+
sgx.trusted_files = [
39+
"file:{{ gramine.libos }}",
40+
"file:{{ entrypoint }}",
41+
"file:{{ gramine.runtimedir() }}/",
42+
"file:{{ arch_libdir }}/",
43+
"file:/usr/{{ arch_libdir }}/",
44+
"file:/usr/lib/swtpm/",
45+
]

0 commit comments

Comments
 (0)