From c1768b1d770e8322f206b65307f522f1f052a6fd Mon Sep 17 00:00:00 2001 From: abelino Date: Thu, 19 Dec 2024 11:20:47 -0800 Subject: [PATCH 1/6] Build swtpm and vendor the tool in the applications priv directory. --- CMakeLists.txt | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 286f4c8..e909107 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,3 +12,51 @@ if(NOT EXISTS ${CPM_PATH}) file(DOWNLOAD ${CPM_RELEASE_URL} ${CPM_PATH}) endif() include(${CPM_PATH}) + +# dependencies +set(LIBTPMS_VER v0.10.0) +set(SWTPM_VER v0.10.0) + +if(DEFINED ENV{LIBTPMS_VER}) + set(LIBTPMS_VER $ENV{LIBTPMS_VER}) +endif() + +if(DEFINED ENV{SWTPM_VER}) + set(SWTPM_VER $ENV{SWTPM_VER}) +endif() + +CPMFindPackage( + NAME libtpms + GITHUB_REPOSITORY stefanberger/libtpms + GIT_TAG ${LIBTPMS_VER}) + +CPMFindPackage( + NAME swtpm + GITHUB_REPOSITORY stefanberger/swtpm + GIT_TAG ${SWTPM_VER}) + +# build +project(swtpm_ex) + +include(ExternalProject) +include(ProcessorCount) + +find_program(MAKE make REQUIRED) + +ProcessorCount(NPROC) + +ExternalProject_Add(libtpms + SOURCE_DIR ${libtpms_SOURCE_DIR} + CONFIGURE_COMMAND ${libtpms_SOURCE_DIR}/autogen.sh + --with-tpm2 + --with-openssl + --prefix=$ENV{MIX_APP_PATH}/priv + BUILD_COMMAND ${MAKE} -j ${NPROC}) + +ExternalProject_Add(swtpm + SOURCE_DIR ${swtpm_SOURCE_DIR} + CONFIGURE_COMMAND ${swtpm_SOURCE_DIR}/autogen.sh + --disable-tests + --with-openssl + --prefix=$ENV{MIX_APP_PATH}/priv + BUILD_COMMAND ${MAKE} -j ${NPROC}) From 871d755cf57ba8197c3c2b9c3d4a26454cedfdf7 Mon Sep 17 00:00:00 2001 From: abelino Date: Thu, 19 Dec 2024 11:22:30 -0800 Subject: [PATCH 2/6] Add child_spec for spawning swtpm via muontrap --- .gitignore | 1 + lib/swtpm.ex | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/.gitignore b/.gitignore index ab3fdd4..93d21b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /_build/ /cover/ +/data/ /deps/ /doc/ /.fetch diff --git a/lib/swtpm.ex b/lib/swtpm.ex index 7308e79..07a055e 100644 --- a/lib/swtpm.ex +++ b/lib/swtpm.ex @@ -2,4 +2,28 @@ defmodule SWTPM do @moduledoc """ Software TPM emulator. """ + + @spec child_spec(term) :: Supervisor.child_spec() + def child_spec(opts) do + state_dir = opts[:state_dir] + server_port = opts[:server_port] || 2321 + ctrl_port = opts[:ctrl_port] || 2322 + + flags = + Keyword.get(opts, :flags, [:not_need_init, :startup_clear]) + |> Enum.map(& to_string(&1) |> String.replace("_", "-")) + |> Enum.join(",") + + MuonTrap.Daemon.child_spec([ + Path.join([:code.priv_dir(:swtpm), "bin", "swtpm"]), + [ + "socket", + "--tpm2", + "--tpmstate", "dir=#{state_dir}", + "--server", "type=tcp,port=#{server_port}", + "--ctrl", "type=tcp,port=#{ctrl_port}", + "--flags", flags + ] + ]) + end end From bef5d17244bade2b32b633b8069facd98586b83c Mon Sep 17 00:00:00 2001 From: abelino Date: Thu, 19 Dec 2024 11:19:19 -0800 Subject: [PATCH 3/6] Add docs --- README.md | 24 ++++++++++++++++++++++++ lib/swtpm.ex | 26 ++++++++++++++++++++++++++ mix.exs | 6 +++--- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 224251f..999f693 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ # swtpm +An easy way to bring a Software TPM emulator into your elixir application by +wrapping [swtpm](https://github.com/stefanberger/swtpm). + +## Prerequisites + +Ensure the following are installed on your host system: +* [libtasn1](https://www.gnu.org/software/libtasn1/) +* [json-glib-1.0](https://wiki.gnome.org/Projects/JsonGlib) +* [libseccomp](https://github.com/seccomp/libseccomp) +* [gmp](https://gmplib.org/) + ## Installation The package can be installed by adding `swtpm` to your list of dependencies @@ -12,3 +23,16 @@ def deps do ] end ``` + +## Supervision + +Add the `SWTPM` module spec to your `application.ex`. For more details see docs +for `&SWTPM.child_spec/1`. + +```elixir +defp children(:host) do + [ + {SWTPM, [state_dir: "data/tpm"]} + ] +end +``` diff --git a/lib/swtpm.ex b/lib/swtpm.ex index 07a055e..afd40a9 100644 --- a/lib/swtpm.ex +++ b/lib/swtpm.ex @@ -3,6 +3,32 @@ defmodule SWTPM do Software TPM emulator. """ + @doc """ + Integrates the `SWTPM` module with a supervision tree, providing a + standardized way to configure and start the Software TPM emulator as a + supervised process. + + ## Options + - `:state_dir` - Specifies the directory where the TPM state will be stored. + + - `:server_port` - Specifies the TCP port used for the TPM server. Defaults + to `2321`. + + - `:ctrl_port` - Specifies the TCP port used for TPM control commands. + Defaults to `2322`. + + - `:flags` - Refer to https://man.archlinux.org/man/swtpm.8.en for full list + of flags. Defaults to `[:not_need_init, :startup_clear]`. + + ## Example + ```elixir + children = [ + {SWTPM, [state_dir: "/data/tpm"]} + ] + + Supervisor.start_link(children, [strategy: :one_for_one, name: MySupervisor]) + ``` + """ @spec child_spec(term) :: Supervisor.child_spec() def child_spec(opts) do state_dir = opts[:state_dir] diff --git a/mix.exs b/mix.exs index 5a09934..9852ba7 100644 --- a/mix.exs +++ b/mix.exs @@ -28,7 +28,7 @@ defmodule SWTPM.MixProject do def application do [ - extra_applications: [:logger] + extra_applications: [:logger], ] end @@ -38,7 +38,7 @@ defmodule SWTPM.MixProject do cookie: "#{@app}_cookie", include_erts: &Nerves.Release.erts/0, steps: [&Nerves.Release.init/1, :assemble], - strip_beams: Mix.env() == :prod or [keep: ["Docs"]] + strip_beams: Mix.env() == :prod or [keep: ["Docs"]], ] end @@ -68,7 +68,7 @@ defmodule SWTPM.MixProject do defp docs do [ main: "readme", - extras: ["README.md"] + extras: ["README.md"], ] end From 1887637def929014398772b8d6696ae79495ddc2 Mon Sep 17 00:00:00 2001 From: abelino Date: Thu, 19 Dec 2024 11:19:43 -0800 Subject: [PATCH 4/6] Fix dialyzer error --- lib/swtpm.ex | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/swtpm.ex b/lib/swtpm.ex index afd40a9..db79da9 100644 --- a/lib/swtpm.ex +++ b/lib/swtpm.ex @@ -29,7 +29,7 @@ defmodule SWTPM do Supervisor.start_link(children, [strategy: :one_for_one, name: MySupervisor]) ``` """ - @spec child_spec(term) :: Supervisor.child_spec() + @spec child_spec(keyword) :: Supervisor.child_spec() def child_spec(opts) do state_dir = opts[:state_dir] server_port = opts[:server_port] || 2321 @@ -40,16 +40,21 @@ defmodule SWTPM do |> Enum.map(& to_string(&1) |> String.replace("_", "-")) |> Enum.join(",") - MuonTrap.Daemon.child_spec([ - Path.join([:code.priv_dir(:swtpm), "bin", "swtpm"]), - [ - "socket", - "--tpm2", - "--tpmstate", "dir=#{state_dir}", - "--server", "type=tcp,port=#{server_port}", - "--ctrl", "type=tcp,port=#{ctrl_port}", - "--flags", flags - ] - ]) + command = Path.join([:code.priv_dir(:swtpm), "bin", "swtpm"]) + args = [ + "socket", + "--tpm2", + "--tpmstate", "dir=#{state_dir}", + "--server", "type=tcp,port=#{server_port}", + "--ctrl", "type=tcp,port=#{ctrl_port}", + "--flags", flags, + ] + + %{ + id: __MODULE__, + start: {MuonTrap.Daemon, :start_link, [command, args, []]}, + type: :worker, + restart: :permanent, + } end end From b8f0f06808cb6d8bba279e5b51075fadfb179085 Mon Sep 17 00:00:00 2001 From: abelino Date: Thu, 19 Dec 2024 11:56:18 -0800 Subject: [PATCH 5/6] Add ci --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 14 ++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8c7262e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +on: push + +jobs: + type_check: + name: Type Check + runs-on: ubuntu-24.04 + steps: + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: "27.1.3" + elixir-version: "1.17.3" + - name: Checkout repository + uses: actions/checkout@v4 + - name: Get dependencies + run: mix deps.get --only dev + - name: Restore PLTs + uses: actions/cache@v4 + with: + path: _build/dev/plt + key: plt-${{ github.ref }}-${{ github.sha }} + restore-keys: | + plt-${{ github.ref }}-${{ github.sha }} + plt-${{ github.ref }}- + plt-refs/heads/master- + - name: Install system deps + run: | + sudo apt install \ + build-essential \ + devscripts \ + equivs \ + libtasn1-6-dev \ + libjson-glib-dev \ + libseccomp-dev \ + libgmp-dev + - name: Compile + run: mix compile + - name: Run dialyzer + run: mix dialyzer diff --git a/CMakeLists.txt b/CMakeLists.txt index e909107..eff4533 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,11 @@ include(ExternalProject) include(ProcessorCount) find_program(MAKE make REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBTASN1 libtasn1 REQUIRED) +pkg_check_modules(LIBJASON_GLIB json-glib-1.0 REQUIRED) +pkg_check_modules(LIBSECCOMP libseccomp REQUIRED) +pkg_check_modules(LIBGMP gmp REQUIRED) ProcessorCount(NPROC) @@ -55,8 +60,9 @@ ExternalProject_Add(libtpms ExternalProject_Add(swtpm SOURCE_DIR ${swtpm_SOURCE_DIR} - CONFIGURE_COMMAND ${swtpm_SOURCE_DIR}/autogen.sh - --disable-tests - --with-openssl - --prefix=$ENV{MIX_APP_PATH}/priv + CONFIGURE_COMMAND PKG_CONFIG_PATH=$ENV{MIX_APP_PATH}/priv/lib/pkgconfig + ${swtpm_SOURCE_DIR}/autogen.sh + --disable-tests + --with-openssl + --prefix=$ENV{MIX_APP_PATH}/priv BUILD_COMMAND ${MAKE} -j ${NPROC}) From 8fc36dc4980cfcd5014848993d1f1481dc3048d0 Mon Sep 17 00:00:00 2001 From: abelino Date: Thu, 19 Dec 2024 14:10:17 -0800 Subject: [PATCH 6/6] Build for ubuntu 22.04, ubuntu 24.04 and macos in CI --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 5 ++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c7262e..df7397d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,54 @@ on: push jobs: + build: + name: Build + strategy: + matrix: + os: + - ubuntu-24.04 + - ubuntu-22.04 + - macos-13 + runs-on: ${{ matrix.os }} + steps: + - name: Set up Elixir + if: ${{ startsWith(matrix.os, 'ubuntu') }} + uses: erlef/setup-beam@v1 + with: + otp-version: "27.1.3" + elixir-version: "1.17.3" + - name: Set up Elixir + if: ${{ startsWith(matrix.os, 'macos') }} + run: brew install elixir + - name: Checkout repository + uses: actions/checkout@v4 + - name: Get dependencies + run: mix deps.get --only dev + - name: Restore PLTs + uses: actions/cache@v4 + with: + path: _build/dev/plt + key: plt-${{ github.ref }}-${{ github.sha }} + restore-keys: | + plt-${{ github.ref }}-${{ github.sha }} + plt-${{ github.ref }}- + plt-refs/heads/master- + - name: Install system deps + if: ${{ startsWith(matrix.os, 'ubuntu') }} + run: | + sudo apt install \ + build-essential \ + devscripts \ + equivs \ + libtasn1-6-dev \ + libjson-glib-dev \ + libseccomp-dev \ + libgmp-dev + - name: Install system deps + if: ${{ startsWith(matrix.os, 'macos') }} + run: brew install automake gawk socat json-glib + - name: Compile + run: mix compile type_check: name: Type Check runs-on: ubuntu-24.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index eff4533..31cf34d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,9 +45,12 @@ find_program(MAKE make REQUIRED) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBTASN1 libtasn1 REQUIRED) pkg_check_modules(LIBJASON_GLIB json-glib-1.0 REQUIRED) -pkg_check_modules(LIBSECCOMP libseccomp REQUIRED) pkg_check_modules(LIBGMP gmp REQUIRED) +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + pkg_check_modules(LIBSECCOMP libseccomp REQUIRED) +endif() + ProcessorCount(NPROC) ExternalProject_Add(libtpms