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 swtpm example #57

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
62 changes: 62 additions & 0 deletions swtpm/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine)

ifeq ($(DEBUG),1)
GRAMINE_LOG_LEVEL = all
else
GRAMINE_LOG_LEVEL = error
endif

ifeq ($(SGX),1)
GRAMINE_ENCRYPTION_KEY = "_sgx_mrenclave"
else
GRAMINE_ENCRYPTION_KEY = "direct"
endif

.PHONY: all
all: swtpm.manifest
ifeq ($(SGX),1)
all: swtpm.manifest.sgx swtpm.sig
endif

swtpm.manifest: swtpm.manifest.template
gramine-manifest \
-Dlog_level=$(GRAMINE_LOG_LEVEL) \
-Dencryption_key=$(GRAMINE_ENCRYPTION_KEY) \
-Darch_libdir=$(ARCH_LIBDIR) \
-Dentrypoint=$(realpath $(shell sh -c "command -v swtpm")) \
$< $@

# gramine-sgx-sign generates both a .sig file and a .manifest.sgx file. This is somewhat
# hard to express properly in Make. The simple solution would be to use
# "Rules with Grouped Targets" (`&:`), however make on Ubuntu <= 20.04 doesn't support it.
#
# Simply using a normal rule with "two targets" is equivalent to creating separate rules
# for each of the targets, and when using `make -j`, this might cause two instances
# of gramine-sgx-sign to get launched simultaneously, potentially breaking the build.
#
# As a workaround, we use a dummy intermediate target, and mark both files as depending on it, to
# get the dependency graph we want. We mark this dummy target as .INTERMEDIATE, which means
# that make will consider the source tree up-to-date even if the sgx_sign file doesn't exist,
# as long as the other dependencies check out. This is in contrast to .PHONY, which would
# be rebuilt on every invocation of make.
swtpm.sig swtpm.manifest.sgx: sgx_sign
@:

.INTERMEDIATE: sgx_sign
sgx_sign: swtpm.manifest
gramine-sgx-sign \
--manifest $< \
--output $<.sgx

ifeq ($(SGX),)
GRAMINE = gramine-direct
else
GRAMINE = gramine-sgx
endif

.PHONY: clean
clean:
$(RM) *.token *.sig *.manifest.sgx *.manifest myvtpm2/.lock myvtpm2/*.permall

.PHONY: distclean
distclean: clean
114 changes: 114 additions & 0 deletions swtpm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# swtpm

This directory contains a Makefile and a manifest template for running swtpm.
See https://github.com/stefanberger/swtpm/.

**NOTE**: Currently works only with PR https://github.com/gramineproject/gramine/pull/1210.
See also https://github.com/stefanberger/swtpm/issues/792.

## Installing

1. Install `libtpms` like this:
https://github.com/stefanberger/libtpms/wiki#build-a-package-on-ubuntu
- Version used: `git checkout v0.9.6`

2. Install `swtpm` like this: https://github.com/stefanberger/swtpm/wiki#compile-on-ubuntu-2104
- Don't install `libtpms-dev` since we've done it already in step 1
- Version used: `git checkout v0.8.0`

Now swtpm tools are installed. We run only `swtpm` executable with Gramine.

## Configuration of `swtpm`

`swtpm` executable can be run in several modes. We hard-code the following configuration
(command-line options) to run with Gramine:
```sh
$ swtpm socket --tpm2 --tpmstate dir=/myvtpm2 --seccomp action=none \
--server type=tcp,port=2321,disconnect --ctrl type=tcp,port=2320 \
--flags not-need-init,startup-clear
```

This configuration means:
- run `swtpm` in TPM2 mode,
- save all TPM state under `/myvtpm2/` dir (encrypted under Gramine with SGX),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Gramine with SGX transparently encrypt file contents? If not you could pass a state encryption key here (though have to keep the encryption key then in a safe place).

- don't use seccomp (Gramine doesn't support it, and it's not needed in Gramine env anyway),
- listen for client connections on TCP/IP port 2321 (in contrast to CUSE or chardev),
- create a control channel on TCP/IP port 2320,
- additional flags for the initial state of TPM.

For more information, see `man swtpm`.

## Building

- `make clean; make` for Gramine without SGX (`gramine-direct`).
- `make clean; make SGX=1` for Gramine with SGX (`gramine-sgx`).

You can add `DEBUG=1` for verbose Gramine logs.

Notice that `gramine-direct` uses a dummy encryption key for TPM files, hard-coded in the manifest.
Whereas `gramine-sgx` uses the MRENCLAVE-based sealing encryption key for TPM files (and is
therefore secure). To make sure the correct key is used, we require a `make clean` step. For details
on how the key is chosen, see Makefile and manifest template.

## Quick tests of swtpm with Gramine

### 1. Self-test

The test idea is taken from https://github.com/stefanberger/swtpm/wiki/Useful-scripts-for-TPM,
Section "Trigger a self-test on a TPM 2 listening on command port 2321 with the disconnect flag".

```sh
# swtpm server in one window
gramine-sgx swtpm

# client script in another window
bash -c "exec 100<>/dev/tcp/localhost/2321; \
echo -en '\x80\x01\x00\x00\x00\x0b\x00\x00\x01\x43\x01' >&100; \
od -tx1 <&100"

## output must be like this:
## 0000000 80 01 00 00 00 0a 00 00 00 00
```

### 2. Hashing in PCR 17

The test idea is taken from the unit test:
https://github.com/stefanberger/swtpm/blob/346b3d62/tests/_test_tpm2_hashing.

```sh
# swtpm server in one window
gramine-sgx swtpm

# client scripts in another window

## 1 step: init TPM to known state
swtpm_ioctl --tcp localhost:2320 -i

## 2 step: startup TPM2
bash -c "exec 100<>/dev/tcp/localhost/2321; \
echo -en '\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00' >&100; \
od -tx1 <&100"

## output must be like this:
## 0000000 80 01 00 00 00 0a 00 00 00 00

## 3 step: ask TPM to hash string "1234" in PCR 17
swtpm_ioctl --tcp localhost:2320 -h 1234

## 4 step: read PCR 17
bash -c "exec 100<>/dev/tcp/localhost/2321; \
echo -en '\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b\x03\x00\x00\x02' >&100; \
od -tx1 <&100"

## output must be like this:
## 0000000 80 01 00 00 00 3e 00 00 00 00 00 00 00 2c 00 00
## 0000020 00 01 00 0b 03 00 00 02 00 00 00 01 00 20 fc a5
## 0000040 d6 49 bf b0 c9 22 fd 33 0f 79 b2 00 43 28 9d af
## 0000060 d6 0d 01 a4 c4 37 3c f2 8a db 56 c9 b4 54

## 5 step: check TPM Established flag (must be 1)
swtpm_ioctl --tcp localhost:2320 -e

## 6 step: shutdown TPM
swtpm_ioctl --tcp localhost:2320 -s
```
Empty file added swtpm/myvtpm2/.dummy
Empty file.
45 changes: 45 additions & 0 deletions swtpm/swtpm.manifest.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# swtpm (TPM emulator) manifest file example

loader.entrypoint = "file:{{ gramine.libos }}"
libos.entrypoint = "{{ entrypoint }}"
loader.log_level = "{{ log_level }}"

loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}"

loader.argv = [
"swtpm", "socket", "--tpm2",
"--tpmstate", "dir=/myvtpm2",
"--seccomp", "action=none",
"--server", "type=tcp,port=2321,disconnect",
"--ctrl", "type=tcp,port=2320",
"--flags", "not-need-init,startup-clear",
]

sys.enable_sigterm_injection = true
sys.enable_extra_runtime_domain_names_conf = true

fs.mounts = [
{ path = "/lib", uri = "file:{{ gramine.runtimedir() }}" },
{ path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" },
{ path = "/usr/{{ arch_libdir }}", uri = "file:/usr/{{ arch_libdir }}" },
{ path = "/usr/lib/swtpm/", uri = "file:/usr/lib/swtpm/" },

{ path = "{{ entrypoint }}", uri = "file:{{ entrypoint }}" },
{ type = "encrypted", path = "/myvtpm2/", uri = "file:myvtpm2/", key_name = "{{ encryption_key }}" },
]

{% if encryption_key == "direct" %}
fs.insecure__keys.direct = "ffeeddccbbaa99887766554433221100"
{% endif %}

sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}
sgx.enclave_size = "1G"

sgx.trusted_files = [
"file:{{ gramine.libos }}",
"file:{{ entrypoint }}",
"file:{{ gramine.runtimedir() }}/",
"file:{{ arch_libdir }}/",
"file:/usr/{{ arch_libdir }}/",
"file:/usr/lib/swtpm/",
]