Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# Gramine
*.token
*.sig
*.manifest.sgx
*.manifest
idscp2-native-*

# SSL
idscp2-examples/src/main/resources/ssl/localhost.p12

# Gradle
.gradle/

Expand Down
44 changes: 44 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine)

SGX_SIGNER_KEY ?= ~/.config/gramine/enclave-key.pem

# This Makefile is targeted towards the client. The server is assumed to run outside SGX.

.PHONY: client
client:
./gradlew nativeBuild -PnativeImageName=idscp2-native-client -PmainNativeClass=de.fhg.aisec.ids.idscp2.example.RunTLSClient
cp idscp2-examples/build/native/nativeCompile/idscp2-native-client .

.PHONY: gramine
gramine: idscp2-native.manifest idscp2-native.manifest.sgx idscp2-native.sig idscp2-native.token

.PHONY: all
all: client gramine

idscp2-native.manifest: idscp2-native.manifest.template
gramine-manifest \
-Darch_libdir=$(ARCH_LIBDIR) \
-Dentrypoint=idscp2-native-client \
$< > $@

idscp2-native.manifest.sgx: idscp2-native.manifest
@test -s $(SGX_SIGNER_KEY) || \
{ echo "SGX signer private key was not found, please specify SGX_SIGNER_KEY!"; exit 1; }
gramine-sgx-sign \
--key $(SGX_SIGNER_KEY) \
--manifest $< \
--output $@

idscp2-native.sig: idscp2-native.manifest.sgx

idscp2-native.token: idscp2-native.sig
gramine-sgx-get-token --output $@ --sig $<

clean-client:
$(RM) idscp2-native-client

clean-gramine:
$(RM) *.token *.sig *.manifest.sgx *.manifest

.PHONY: clean
clean: clean-client clean-gramine
158 changes: 158 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,161 @@ This is the official Kotlin-based implementation of the IDS Communication Protoc
Maven artifacts are pushed to maven central and can be found here: https://search.maven.org/search?q=idscp2.

More information about the usage can be found in the [IDSCP2 Documentation](https://github.com/industrial-data-space/idscp2-jvm/wiki).

## Intel SGX Support

IDSCP2 can make use of Intel SGX to attest Client nodes upon Server requests using the _Intel Attestation Services (IAS)_. Before the handshake is built, the Server sends the Client a nonce, which must be included in an SGX quote. When the Server receives the quote, it sends it to IAS and verifies whether the quote's payload corresponds to the initial nonce value. If both checks pass, the Server accepts the Client's handshake.

### System Requirements

- [Ubuntu 20.04](https://releases.ubuntu.com/focal/) or above
- [Intel SGX SDK](https://github.com/intel/linux-sgx)
- [GraalVM for Java 17](https://github.com/graalvm/graalvm-ce-builds/releases)
- [Gramine SGX](https://github.com/gramineproject/gramine)

### Installation Guide

In this section we detail the setup process for SGX support of a system running **Ubuntu 22.04**.

#### Intel SGX SDK

1. We start by setting up the SGX SDK. The current stable version is **2.18.1**. We first install all necessary dependencies:
```bash
sudo apt-get install build-essential ocaml ocamlbuild automake autoconf libtool wget python-is-python3 \
libssl-dev git cmake perl libssl-dev libcurl4-openssl-dev protobuf-compiler \
libprotobuf-dev debhelper cmake reprepro unzip pkgconf libboost-dev libboost-system-dev libboost-thread-dev \
protobuf-c-compiler libprotobuf-c-dev lsb-release libsystemd0 python2
```

2. We can now clone the SDK repository and start building it:
```bash
git clone https://github.com/intel/linux-sgx.git
cd linux-sgx && make preparation
sudo cp external/toolset/ubuntu20.04/* /usr/local/bin
make sdk_install_pkg
```

3. To be able to use Gramine SGX, we will also need the _Intel Platform SoftWare (PSW)_:
```bash
make psw
make deb_psw_pkg
```

4. Installing the SDK is first done through the generated installer. The PSW is composed of multiple `.deb` packages which will be installed individually afterward.
```bash
cd linux/installer/bin
sudo ./sgx_linux_x64_sdk_2.18.101.1.bin --prefix /opt/intel
source /opt/intel/sgxsdk/environment
cd ../../../ && cd linux/installer/deb
```

Note that the PSW packages cannot be installed in a random order, since many are the dependencies of others. Therefore, we choose the following order of installation:
- `libsgx-headers`
- `libsgx-launch`
- `libsgx-enclave-common`
- `libsgx-epid`
- `libsgx-quote-ex`
- `libsgx-uae-service`
- `libsgx-urts`
- `sgx-aesm-service/libsgx-ra-network`
- `sgx-aesm-service/libsgx-ra-uefi`
- `sgx-asem-service/libsgx-dcap-default-qpl`
- `sgx-aesm-service/libsgx-dcap-quote-verify`
- `sgx-aesm-service/libsgx-ae-*`
- `sgx-aesm-service/libsgx-pce-logic`
- `sgx-aesm-service/libsgx-qe3-logic`
- `sgx-asem-service/libsgx-dcap-ql`
- `sgx-aesm-service/sgx-aesm-service`
- `sgx-aesm-service/libsgx-aesm-*`

The installation of a package can be performed with the following command:
```bash
sudo apt install ./<package-name>.deb
```

#### GraalVM for Java 17

1. The latest version of GraalVM upon writing this document is **22.3.0**. We download the GraalVM archive using the following download [link](https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.3.0/graalvm-ce-java17-linux-amd64-22.3.0.tar.gz). We then extract the contents using:
```bash
tar -xzf graalvm-ce-java17-linux-amd64-22.3.0.tar.gz
```

2. To be able to use Java, we need to adapt two global variables: `PATH` and `JAVA_HOME`. To accommodate this, we choose to include the following lines at the end of the `.bashrc` file:
```bash
export PATH=/home/ubuntu/graalvm-ce-java17-22.3.0/bin:$PATH
export JAVA_HOME=/home/ubuntu/graalvm-ce-java17-22.3.0
```
We now need to recompile it for the changes to take effect:
```bash
source .bashrc
```

3. To compile the Kotlin codebase, we need Native Image:
```bash
gu install native-image
```

#### Gramine SGX

1. We start by installing all dependencies:
```bash
sudo apt-get install -y build-essential \
autoconf bison gawk nasm ninja-build pkg-config python3 python3-click meson \
python3-jinja2 python3-pip python3-pyelftools wget libunwind8 musl-tools \
python3-pytest libgmp-dev libmpfr-dev libmpc-dev libisl-dev python3-protobuf
```

2. We can now clone Gramine. The version used for testing is **1.3.1**:
```bash
git clone https://github.com/gramineproject/gramine.git
cd gramine && git checkout v1.3.1
```

3. We finally move on to the building and installation process:
```bash
meson setup build/ --buildtype=release -Ddirect=enabled -Dsgx=enabled --prefix=/usr
ninja -C build/
sudo ninja -C build/ install
```

> Note that we use a custom installation path for installing Gramine. This is due to a bug in version **1.3.1** on Ubuntu 22.04 which prevents Python from finding `graminelibos`. If this does not solve the problem, try also exporting the global variable ``PYTHONPATH`` as follows:
> ```bash
> export PYTHONPATH=$PYTHONPATH:/usr/local/graminelibos
> ```

### Execution Instructions

Before building and running either of the two parties, we must provide **4** pieces of information to the underlying codebase:
1. In [idscp2-native.manifest.template](idscp2-native.manifest.template), insert the **SPID** of the _Intel SGX Attestation Service (Linkable)_ subscription,
2. In [GramineRaVerifier.kt](idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/defaultdrivers/remoteattestation/gramine/GramineRaVerifier.kt), insert the corresponding **Primary Key**,
3. In the directory [idscp2-examples/src/main/resources/ssl](idscp2-examples/src/main/resources/ssl), insert the **Key Store** file named `localhost.p12`.
4. In the file [expected-client-mrenclave.txt](expected-client-mrenclave.txt), insert the **MRENCLAVE** value obtained after compiling and signing the executable (see below).

First, we build the Client using the given Makefile:
```bash
make all
```

After it is finished, the output of the build process should contain the following lines:
```
Attributes:
mr_enclave: <32-byte-string-in-hex-format>
.....

```

We copy the MRENCLAVE value and insert it into the [expected-client-mrenclave.txt](expected-client-mrenclave.txt) file. This will ensure that the server will not accept connections to clients with different builds or altered in any way.

> Note that, in production, our goal is not to add the expected MRENCLAVE value to a file, but to **extract it directly from the DAT** (new field in the JWT body)!

Having configured all our parameters, we can now execute IDSCP2 using Intel SGX. From the root directory of this project, we first run the Server:
```bash
./gradlew run
```

After the build process is done, we run the Client from a separate command prompt:
```bash
sudo gramine-sgx idscp2-native
```

If all went well, both parties should display a successful handshake.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ object Utils {

lateinit var senderAgentProducer: () -> URI
lateinit var issuerProducer: () -> URI

@Suppress("MemberVisibilityCanBePrivate")
lateinit var infomodelVersion: String
var dapsUrlProducer: () -> String = { Constants.DEFAULT_DAPS_URL }
Expand Down
1 change: 1 addition & 0 deletions expected-client-mrenclave.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<insert-expected-mrenclave-value-here>
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
/*-
* ========================LICENSE_START=================================
* idscp2-api
* %%
* Copyright (C) 2023 Fraunhofer AISEC
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package de.fhg.aisec.ids.idscp2.api

import java.security.MessageDigest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ class FSMImpl<CC : Idscp2Connection>(
override val remotePeer: String
get() = secureChannel.remotePeer()

fun remotePeer(): String {
return secureChannel.remotePeer()
}

private fun checkForFsmCycles() {
// check if current thread holds already the fsm lock, then we have a circle
// this runs into an issue: onControlMessage must be called only from other threads!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*-
* ========================LICENSE_START=================================
* idscp2-core
* %%
* Copyright (C) 2022 Fraunhofer AISEC
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine

import de.fhg.aisec.ids.idscp2.api.drivers.RaProverDriver
import de.fhg.aisec.ids.idscp2.api.fsm.InternalControlMessage
import de.fhg.aisec.ids.idscp2.api.fsm.RaProverFsmListener
import org.slf4j.LoggerFactory
import java.io.File
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue

/**
* An RaProver that, when ran by the client, produces an Intel SGX Attestation Report
* containing the nonce sent by the RaVerifier.
*
* @author Andrei-Cosmin Aprodu (andrei-cosmin.aprodu@aisec.fraunhofer.de)
*/
class GramineRaProver(fsmListener: RaProverFsmListener) : RaProverDriver<String>(fsmListener) {
private val queue: BlockingQueue<ByteArray> = LinkedBlockingQueue()
private lateinit var currentTarget: String

override fun delegate(message: ByteArray) {
queue.add(message)
if (LOG.isDebugEnabled) {
LOG.debug("Delegated to prover")
}
}

override fun setConfig(config: String) {
currentTarget = config
}

override fun run() {
// Only the client can issue an attestation certificate, so we cannot consider
// the current authentication process symmetrical anymore.
if (currentTarget == "Server") {
fsmListener.onRaProverMessage(InternalControlMessage.RA_PROVER_OK)
return
}

try {
val msg = queue.take().decodeToString()
if (LOG.isDebugEnabled) {
LOG.debug("Prover received nonce. Generating certificate...")
}

// https://gramine.readthedocs.io/en/stable/attestation.html
File("/dev/attestation/user_report_data").writeText(msg)
val quote = File("/dev/attestation/quote").readBytes()

if (LOG.isDebugEnabled) {
LOG.debug("Prover sends certificate...")
}
fsmListener.onRaProverMessage(
InternalControlMessage.RA_PROVER_MSG,
quote
)
} catch (e: InterruptedException) {
if (running) {
fsmListener.onRaProverMessage(InternalControlMessage.RA_PROVER_FAILED)
}
return
}
fsmListener.onRaProverMessage(InternalControlMessage.RA_PROVER_OK)
}

companion object {
const val GRAMINE_RA_PROVER_ID = "Gramine"
private val LOG = LoggerFactory.getLogger(GramineRaProver::class.java)
}
}
Loading