diff --git a/.gitignore b/.gitignore index b9f68e5..8790d0e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ +# Gramine +*.token +*.sig +*.manifest.sgx +*.manifest +idscp2-native-* + +# SSL +idscp2-examples/src/main/resources/ssl/localhost.p12 + # Gradle .gradle/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c6f7d08 --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 979e1b6..5a65b7c 100644 --- a/README.md +++ b/README.md @@ -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 ./.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. diff --git a/camel-idscp2/src/main/kotlin/de/fhg/aisec/ids/camel/idscp2/Utils.kt b/camel-idscp2/src/main/kotlin/de/fhg/aisec/ids/camel/idscp2/Utils.kt index c9931f9..700aa0a 100644 --- a/camel-idscp2/src/main/kotlin/de/fhg/aisec/ids/camel/idscp2/Utils.kt +++ b/camel-idscp2/src/main/kotlin/de/fhg/aisec/ids/camel/idscp2/Utils.kt @@ -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 } diff --git a/expected-client-mrenclave.txt b/expected-client-mrenclave.txt new file mode 100644 index 0000000..8904881 --- /dev/null +++ b/expected-client-mrenclave.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/idscp2-api/src/main/kotlin/de/fhg/aisec/ids/idscp2/api/FingerprintUtils.kt b/idscp2-api/src/main/kotlin/de/fhg/aisec/ids/idscp2/api/FingerprintUtils.kt index 80a8176..1dd49ff 100644 --- a/idscp2-api/src/main/kotlin/de/fhg/aisec/ids/idscp2/api/FingerprintUtils.kt +++ b/idscp2-api/src/main/kotlin/de/fhg/aisec/ids/idscp2/api/FingerprintUtils.kt @@ -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 diff --git a/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/core/fsm/FSMImpl.kt b/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/core/fsm/FSMImpl.kt index b27184c..a11a1d6 100644 --- a/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/core/fsm/FSMImpl.kt +++ b/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/core/fsm/FSMImpl.kt @@ -164,6 +164,10 @@ class FSMImpl( 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! diff --git a/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/defaultdrivers/remoteattestation/gramine/GramineRaProver.kt b/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/defaultdrivers/remoteattestation/gramine/GramineRaProver.kt new file mode 100644 index 0000000..5f7aa0b --- /dev/null +++ b/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/defaultdrivers/remoteattestation/gramine/GramineRaProver.kt @@ -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(fsmListener) { + private val queue: BlockingQueue = 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) + } +} diff --git a/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/defaultdrivers/remoteattestation/gramine/GramineRaVerifier.kt b/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/defaultdrivers/remoteattestation/gramine/GramineRaVerifier.kt new file mode 100644 index 0000000..bad4d83 --- /dev/null +++ b/idscp2-core/src/main/kotlin/de/fhg/aisec/ids/idscp2/defaultdrivers/remoteattestation/gramine/GramineRaVerifier.kt @@ -0,0 +1,144 @@ +/*- + * ========================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.RaVerifierDriver +import de.fhg.aisec.ids.idscp2.api.fsm.InternalControlMessage +import de.fhg.aisec.ids.idscp2.api.fsm.RaVerifierFsmListener +import org.slf4j.LoggerFactory +import java.io.File +import java.lang.ProcessBuilder +import java.security.SecureRandom +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingQueue + +/** + * A RaVerifier that requires the client RaProver to present a valid Intel SGX Attestation Report + * containing a nonce it sends in the beginning. + * + * @author Andrei-Cosmin Aprodu (andrei-cosmin.aprodu@aisec.fraunhofer.de) + */ +class GramineRaVerifier(fsmListener: RaVerifierFsmListener) : RaVerifierDriver(fsmListener) { + private val queue: BlockingQueue = LinkedBlockingQueue() + private lateinit var currentTarget: String + + // TODO: Insert Primary Key corresponding to the current SPID here! + private val primaryKey = "" + + private fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } + + override fun delegate(message: ByteArray) { + queue.add(message) + if (LOG.isDebugEnabled) { + LOG.debug("Delegated to Gramine Verifier") + } + } + + 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 symmatrical anymore. + if (currentTarget == "Client") { + fsmListener.onRaVerifierMessage(InternalControlMessage.RA_VERIFIER_OK) + return + } + if (primaryKey == "") { + LOG.error("Please enter your Primary Key in order to access the Intel Attestation Services!") + fsmListener.onRaVerifierMessage(InternalControlMessage.RA_VERIFIER_FAILED) + return + } + + val nonceRaw = ByteArray(32) + SecureRandom().nextBytes(nonceRaw) + val nonce = nonceRaw.toHexString() + + try { + if (LOG.isDebugEnabled) { + LOG.debug("Verifier sends nonce \"${nonce}\"...") + } + fsmListener.onRaVerifierMessage( + InternalControlMessage.RA_VERIFIER_MSG, + nonce.toByteArray() + ) + if (LOG.isDebugEnabled) { + LOG.debug("Verifier waits....") + } + val msg = queue.take() + if (LOG.isDebugEnabled) { + LOG.debug("Verifier received response. Starting checks...") + } + + File("/tmp/QUOTE").writeBytes(msg) + + // 1st check: verify whether the quote is authentic + val quoteVerifierProcess = ProcessBuilder("../quote-verifier.sh", primaryKey).start() + quoteVerifierProcess.waitFor() + if (String(quoteVerifierProcess.inputStream.readAllBytes()).trim().toInt() != 1) { + LOG.error("Check 1: Quote not authentic! Aborting...") + if (running) { + fsmListener.onRaVerifierMessage(InternalControlMessage.RA_VERIFIER_FAILED) + } + return + } else { + LOG.info("Check 1: Quote authentic.") + } + + val quoteContents = File("/tmp/QUOTE").readBytes() + + // 2nd check: verify whether nonce is included in quote + if (quoteContents.copyOfRange(368, 432).toString(Charsets.US_ASCII) != nonce) { + LOG.error("Check 2: Quote does not contain nonce! Aborting...") + if (running) { + fsmListener.onRaVerifierMessage(InternalControlMessage.RA_VERIFIER_FAILED) + } + return + } else { + LOG.info("Check 2: Quote contains nonce.") + } + + // 3rd check: verify whether MRENCLAVE (enclave measurement hash) is the expected one + // TODO: rather than extracting the expected MRENCLAVE from a file, extract it from a new DAT field. + val expectedMeasurement = File("../expected-client-mrenclave.txt").readBytes().toString(Charsets.US_ASCII).trim() + if (quoteContents.copyOfRange(112, 144).toHexString() != expectedMeasurement) { + LOG.error("Check 3: Client MRENCLAVE does not match expected value! Aborting...") + if (running) { + fsmListener.onRaVerifierMessage(InternalControlMessage.RA_VERIFIER_FAILED) + } + return + } else { + LOG.info("Check 3: Quote contains valid MRENCLAVE.") + } + } catch (e: InterruptedException) { + if (running) { + fsmListener.onRaVerifierMessage(InternalControlMessage.RA_VERIFIER_FAILED) + } + return + } + fsmListener.onRaVerifierMessage(InternalControlMessage.RA_VERIFIER_OK) + } + + companion object { + const val GRAMINE_RA_VERIFIER_ID = "Gramine" + private val LOG = LoggerFactory.getLogger(GramineRaVerifier::class.java) + } +} diff --git a/idscp2-core/src/test/kotlin/de/fhg/aisec/ids/idscp2/tests/Idscp2Integration.kt b/idscp2-core/src/test/kotlin/de/fhg/aisec/ids/idscp2/tests/Idscp2Integration.kt index 968566e..e65322e 100644 --- a/idscp2-core/src/test/kotlin/de/fhg/aisec/ids/idscp2/tests/Idscp2Integration.kt +++ b/idscp2-core/src/test/kotlin/de/fhg/aisec/ids/idscp2/tests/Idscp2Integration.kt @@ -45,7 +45,6 @@ import org.junit.Assert import org.junit.Test import java.nio.charset.StandardCharsets import java.nio.file.Paths -import java.security.cert.X509Certificate import java.util.Objects import java.util.concurrent.BlockingQueue import java.util.concurrent.CountDownLatch @@ -71,7 +70,7 @@ class Idscp2Integration { override val renewalThreshold = 1.0f - override fun verifyToken(dat: ByteArray, peerCertificate: X509Certificate?) = + override fun verifyToken(dat: ByteArray, peerCertificateFingerprint: String): VerifiedDat = throw DatException("DapsRejector will reject each token") } @@ -81,7 +80,7 @@ class Idscp2Integration { override val renewalThreshold = 1.0f - override fun verifyToken(dat: ByteArray, peerCertificate: X509Certificate?): VerifiedDat { + override fun verifyToken(dat: ByteArray, peerCertificateFingerprint: String): VerifiedDat { if (dat.contentEquals(VALID_DAT)) { return VerifiedDat(VALID_DAT, "IDENTITY", (System.currentTimeMillis() / 1000) + 3600) } else { @@ -95,7 +94,7 @@ class Idscp2Integration { override val renewalThreshold = 1.0f - override fun verifyToken(dat: ByteArray, peerCertificate: X509Certificate?): VerifiedDat { + override fun verifyToken(dat: ByteArray, peerCertificateFingerprint: String): VerifiedDat { if (dat.contentEquals(VALID_DAT)) { return VerifiedDat(VALID_DAT, "IDENTITY", (System.currentTimeMillis() / 1000) + delay) } else { diff --git a/idscp2-examples/build.gradle.kts b/idscp2-examples/build.gradle.kts index 27da837..ae05ac8 100644 --- a/idscp2-examples/build.gradle.kts +++ b/idscp2-examples/build.gradle.kts @@ -1,5 +1,6 @@ plugins { application + id("org.graalvm.buildtools.native") version "0.9.11" } version = libs.versions.idscp2.get() @@ -22,3 +23,21 @@ application { ?: "de.fhg.aisec.ids.idscp2.example.RunTLSServer" ) } + +graalvmNative { + binaries { + named("main") { + imageName.set( + findProperty("nativeImageName")?.toString() + ?: "idscp2-native" + ) + mainClass.set( + findProperty("mainNativeClass")?.toString() + ?: "de.fhg.aisec.ids.idscp2.example.RunTLSServer" + ) + runtimeArgs.add("--report-unsupported-elements-at-runtime") + buildArgs.add("-H:ReflectionConfigurationFiles=../../../src/main/resources/reflect-config.json") + buildArgs.add("-H:ResourceConfigurationFiles=../../../src/main/resources/resource-config.json") + } + } +} diff --git a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ClientInitiator.kt b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ClientInitiator.kt index c883b92..1dd0225 100644 --- a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ClientInitiator.kt +++ b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ClientInitiator.kt @@ -25,8 +25,8 @@ import de.fhg.aisec.ids.idscp2.api.connection.Idscp2ConnectionAdapter import de.fhg.aisec.ids.idscp2.api.raregistry.RaProverDriverRegistry import de.fhg.aisec.ids.idscp2.api.raregistry.RaVerifierDriverRegistry import de.fhg.aisec.ids.idscp2.core.connection.Idscp2ConnectionImpl -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaProver -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaVerifier +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaProver +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaVerifier import de.fhg.aisec.ids.idscp2.defaultdrivers.securechannel.tls13.NativeTLSDriver import de.fhg.aisec.ids.idscp2.defaultdrivers.securechannel.tls13.NativeTlsConfiguration import org.slf4j.LoggerFactory @@ -40,15 +40,15 @@ class Idscp2ClientInitiator { // register ra drivers RaProverDriverRegistry.registerDriver( - DemoRaProver.DEMO_RA_PROVER_ID, - ::DemoRaProver, - null + GramineRaProver.GRAMINE_RA_PROVER_ID, + ::GramineRaProver, + "Client" ) RaVerifierDriverRegistry.registerDriver( - DemoRaVerifier.DEMO_RA_VERIFIER_ID, - ::DemoRaVerifier, - null + GramineRaVerifier.GRAMINE_RA_VERIFIER_ID, + ::GramineRaVerifier, + "Client" ) // connect to idscp2 server diff --git a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ServerInitiator.kt b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ServerInitiator.kt index 884f082..99047e5 100644 --- a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ServerInitiator.kt +++ b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/Idscp2ServerInitiator.kt @@ -27,8 +27,8 @@ import de.fhg.aisec.ids.idscp2.api.raregistry.RaProverDriverRegistry import de.fhg.aisec.ids.idscp2.api.raregistry.RaVerifierDriverRegistry import de.fhg.aisec.ids.idscp2.api.server.Idscp2ServerFactory import de.fhg.aisec.ids.idscp2.core.connection.Idscp2ConnectionImpl -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaProver -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaVerifier +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaProver +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaVerifier import de.fhg.aisec.ids.idscp2.defaultdrivers.securechannel.tls13.NativeTLSDriver import de.fhg.aisec.ids.idscp2.defaultdrivers.securechannel.tls13.NativeTlsConfiguration import org.slf4j.LoggerFactory @@ -41,15 +41,15 @@ class Idscp2ServerInitiator : Idscp2EndpointListener { // register ra drivers RaProverDriverRegistry.registerDriver( - DemoRaProver.DEMO_RA_PROVER_ID, - ::DemoRaProver, - null + GramineRaProver.GRAMINE_RA_PROVER_ID, + ::GramineRaProver, + "Server" ) RaVerifierDriverRegistry.registerDriver( - DemoRaVerifier.DEMO_RA_VERIFIER_ID, - ::DemoRaVerifier, - null + GramineRaVerifier.GRAMINE_RA_VERIFIER_ID, + ::GramineRaVerifier, + "Server" ) // create server config diff --git a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSClient.kt b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSClient.kt index 830bd81..a4fa27d 100644 --- a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSClient.kt +++ b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSClient.kt @@ -23,58 +23,51 @@ import de.fhg.aisec.ids.idscp2.api.configuration.AttestationConfig import de.fhg.aisec.ids.idscp2.api.configuration.Idscp2Configuration import de.fhg.aisec.ids.idscp2.daps.aisecdaps.AisecDapsDriver import de.fhg.aisec.ids.idscp2.daps.aisecdaps.AisecDapsDriverConfig -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaProver -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaVerifier +import de.fhg.aisec.ids.idscp2.daps.aisecdaps.SecurityProfile +import de.fhg.aisec.ids.idscp2.daps.aisecdaps.SecurityRequirements +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaProver +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaVerifier import de.fhg.aisec.ids.idscp2.defaultdrivers.securechannel.tls13.NativeTlsConfiguration -import de.fhg.aisec.ids.idscp2.keystores.KeyStoreUtil.loadKeyStore import java.nio.file.Paths -import java.util.Objects object RunTLSClient { @JvmStatic fun main(args: Array) { - val keyStorePath = Paths.get( - Objects.requireNonNull( - RunTLSClient::class.java.classLoader - .getResource("ssl/consumer-keystore.p12") - ).path - ) - - val trustStorePath = Paths.get( - Objects.requireNonNull( - RunTLSClient::class.java.classLoader - .getResource("ssl/truststore.p12") - ).path - ) + // absolute paths to facilitate native-image compilation + // TODO: Key Store file 'localhost.p12' missing and must be provided! + val keyStorePath = Paths.get("idscp2-examples/src/main/resources/ssl/localhost.p12") + val trustStorePath = Paths.get("idscp2-examples/src/main/resources/ssl/truststore.p12") val localAttestationConfig = AttestationConfig.Builder() - .setSupportedRaSuite(arrayOf(DemoRaProver.DEMO_RA_PROVER_ID)) - .setExpectedRaSuite(arrayOf(DemoRaVerifier.DEMO_RA_VERIFIER_ID)) + .setSupportedRaSuite(arrayOf(GramineRaProver.GRAMINE_RA_PROVER_ID)) + .setExpectedRaSuite(arrayOf(GramineRaVerifier.GRAMINE_RA_VERIFIER_ID)) .setRaTimeoutDelay(300 * 1000L) // 300 seconds .build() val password = "password".toCharArray() - // Load certificates from local KeyStore - val ks = loadKeyStore(keyStorePath, password) + // create daps driver + val securityRequirements = SecurityRequirements.Builder() + .setRequiredSecurityLevel(SecurityProfile.INVALID) + .build() val dapsDriver = AisecDapsDriver( AisecDapsDriverConfig.Builder() .setKeyStorePath(keyStorePath) .setKeyStorePassword(password) .setKeyPassword(password) - .setKeyAlias("1") .setTrustStorePath(trustStorePath) .setTrustStorePassword(password) - .setDapsUrl("https://daps-dev.aisec.fraunhofer.de/v4") - .loadTransportCertsFromKeystore(ks) + .setKeyAlias("1") + .setDapsUrl("https://daps.aisec.fraunhofer.de") + .setSecurityRequirements(securityRequirements) .build() ) // create idscp2 config val settings = Idscp2Configuration.Builder() - .setAckTimeoutDelay(500) // 500 ms - .setHandshakeTimeoutDelay(5 * 1000L) // 5 seconds + .setAckTimeoutDelay(20 * 1000L) // 20 seconds + .setHandshakeTimeoutDelay(50 * 1000L) // 50 seconds .setAttestationConfig(localAttestationConfig) .setDapsDriver(dapsDriver) .build() @@ -87,7 +80,7 @@ object RunTLSClient { .setTrustStorePath(trustStorePath) .setTrustStorePassword(password) .setCertificateAlias("1.0.1") - .setHost("provider-core") + .setServerPort(29292) .build() val initiator = Idscp2ClientInitiator() diff --git a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSServer.kt b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSServer.kt index fed4026..5ca188d 100644 --- a/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSServer.kt +++ b/idscp2-examples/src/main/kotlin/de/fhg/aisec/ids/idscp2/example/RunTLSServer.kt @@ -23,20 +23,22 @@ import de.fhg.aisec.ids.idscp2.api.configuration.AttestationConfig import de.fhg.aisec.ids.idscp2.api.configuration.Idscp2Configuration import de.fhg.aisec.ids.idscp2.daps.aisecdaps.AisecDapsDriver import de.fhg.aisec.ids.idscp2.daps.aisecdaps.AisecDapsDriverConfig -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaProver -import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.demo.DemoRaVerifier +import de.fhg.aisec.ids.idscp2.daps.aisecdaps.SecurityProfile +import de.fhg.aisec.ids.idscp2.daps.aisecdaps.SecurityRequirements +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaProver +import de.fhg.aisec.ids.idscp2.defaultdrivers.remoteattestation.gramine.GramineRaVerifier import de.fhg.aisec.ids.idscp2.defaultdrivers.securechannel.tls13.NativeTlsConfiguration -import de.fhg.aisec.ids.idscp2.keystores.KeyStoreUtil.loadKeyStore import java.nio.file.Paths import java.util.Objects object RunTLSServer { @JvmStatic fun main(argv: Array) { + // TODO: Key Store file 'localhost.p12' missing and must be provided! val keyStorePath = Paths.get( Objects.requireNonNull( RunTLSServer::class.java.classLoader - .getResource("ssl/provider-keystore.p12") + .getResource("ssl/localhost.p12") ).path ) @@ -48,30 +50,34 @@ object RunTLSServer { ) val localAttestationConfig = AttestationConfig.Builder() - .setSupportedRaSuite(arrayOf(DemoRaProver.DEMO_RA_PROVER_ID)) - .setExpectedRaSuite(arrayOf(DemoRaVerifier.DEMO_RA_VERIFIER_ID)) + .setSupportedRaSuite(arrayOf(GramineRaProver.GRAMINE_RA_PROVER_ID)) + .setExpectedRaSuite(arrayOf(GramineRaVerifier.GRAMINE_RA_VERIFIER_ID)) .setRaTimeoutDelay(300 * 1000L) // 300 seconds .build() val password = "password".toCharArray() - // Load certificates from local KeyStore - val ks = loadKeyStore(keyStorePath, password) + // create daps config + val securityRequirements = SecurityRequirements.Builder() + .setRequiredSecurityLevel(SecurityProfile.INVALID) + .build() val dapsDriver = AisecDapsDriver( AisecDapsDriverConfig.Builder() .setKeyStorePath(keyStorePath) .setKeyStorePassword(password) .setKeyPassword(password) - .setKeyAlias("1") .setTrustStorePath(trustStorePath) .setTrustStorePassword(password) - .setDapsUrl("https://daps-dev.aisec.fraunhofer.de/v4") - .loadTransportCertsFromKeystore(ks) + .setKeyAlias("1") + .setDapsUrl("https://daps.aisec.fraunhofer.de") + .setSecurityRequirements(securityRequirements) .build() ) val settings = Idscp2Configuration.Builder() + .setAckTimeoutDelay(20 * 1000L) // 20 seconds + .setHandshakeTimeoutDelay(50 * 1000L) // 50 seconds .setAttestationConfig(localAttestationConfig) .setDapsDriver(dapsDriver) .build() @@ -83,7 +89,7 @@ object RunTLSServer { .setTrustStorePath(trustStorePath) .setTrustStorePassword(password) .setCertificateAlias("1.0.1") - .setHost("consumer-core") + .setServerPort(29292) .build() val initiator = Idscp2ServerInitiator() diff --git a/idscp2-examples/src/main/resources/reflect-config.json b/idscp2-examples/src/main/resources/reflect-config.json new file mode 100644 index 0000000..6581e8a --- /dev/null +++ b/idscp2-examples/src/main/resources/reflect-config.json @@ -0,0 +1,60 @@ +[ + { + "name": "kotlin.reflect.jvm.internal.ReflectionFactoryImpl", + "allDeclaredConstructors": true + }, + { + "name": "kotlin.KotlinVersion", + "allPublicMethods": true, + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "kotlin.KotlinVersion[]" + }, + { + "name": "kotlin.KotlinVersion$Companion" + }, + { + "name": "kotlin.KotlinVersion$Companion[]" + }, + { + "name": "com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "io.jsonwebtoken.impl.DefaultJwtBuilder", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "io.ktor.utils.io.pool.DefaultPool", + "fields": [ + { + "name": "top" + } + ] + }, + { + "name": "kotlin.internal.jdk8.JDK8PlatformImplementations", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + } +] + + + diff --git a/idscp2-examples/src/main/resources/resource-config.json b/idscp2-examples/src/main/resources/resource-config.json new file mode 100644 index 0000000..faf2a5c --- /dev/null +++ b/idscp2-examples/src/main/resources/resource-config.json @@ -0,0 +1,13 @@ +{ + "resources": [ + { + "pattern": "META-INF/.*.kotlin_module$" + }, + { + "pattern": "META-INF/services/.*" + }, + { + "pattern": ".*.kotlin_builtins" + } + ] +} \ No newline at end of file diff --git a/idscp2-native.manifest.template b/idscp2-native.manifest.template new file mode 100644 index 0000000..9fb4a49 --- /dev/null +++ b/idscp2-native.manifest.template @@ -0,0 +1,48 @@ +libos.entrypoint = "{{ entrypoint }}" + +loader.entrypoint = "file:{{ gramine.libos }}" +loader.log_level = "error" +loader.argv0_override = "" + +#sys.stack.size = "256M" +#sys.brk.max_size = "512M" +sys.enable_sigterm_injection = true +sys.insecure__allow_eventfd = true + +loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/lib:/usr/{{ arch_libdir }}" + +fs.mounts = [ + {type = "chroot", path = "/lib", uri = "file:{{ gramine.runtimedir() }}"}, + {type = "chroot", path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}"}, + {type = "tmpfs", path = "/tmp", uri = "file:/tmp"}, + {path = "/etc/resolv.conf", uri = "file:/etc/resolv.conf"}, + {path = "/etc/nsswitch.conf", uri = "file:/etc/nsswitch.conf"}, + {path = "/etc/passwd", uri = "file:/etc/passwd"}, + {path = "/etc/timezone", uri = "file:/etc/timezone"}, + {path = "/etc/host.conf", uri = "file:/etc/host.conf"}, + {path = "/etc/hosts", uri = "file:/etc/hosts"}, +] + +sgx.thread_num = 32 +sgx.nonpie_binary = true +sgx.enclave_size = "512M" + +sgx.remote_attestation = "epid" +sgx.ra_client_linkable = true + +# TODO: Insert SPID from https://api.portal.trustedservices.intel.com/developer here! +sgx.ra_client_spid = "" + +sgx.trusted_files = [ + "file:{{ entrypoint }}", + "file:{{ gramine.libos }}", + "file:{{ gramine.runtimedir() }}/", + "file:{{ arch_libdir }}/", + "file:idscp2-examples/src/main/resources/ssl/", + "file:/etc/resolv.conf", + "file:/etc/nsswitch.conf", + "file:/etc/passwd", + "file:/etc/timezone", + "file:/etc/host.conf", + "file:/etc/hosts", +] diff --git a/quote-verifier.sh b/quote-verifier.sh new file mode 100755 index 0000000..2d009ff --- /dev/null +++ b/quote-verifier.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +report="/tmp/IAS_REPORT" +sig="/tmp/IAS_SIG" +quote="/tmp/QUOTE" + +touch $report +touch $sig + +if [ ! -f $quote ]; then + echo 2 + exit +fi + +# Use Linkable Primary Key +res1=$(gramine-sgx-ias-request report -q $quote -k $1 -r $report -s $sig) +res2=$(gramine-sgx-ias-verify-report -r $report -s $sig --allow-outdated-tcb) + +if [[ $res2 == *"IAS report: signature verified correctly"* ]]; then + echo 1 + exit +else + echo $res1 >> IAS_OUTPUT + echo $res2 > IAS_OUTPUT + echo 0 + exit +fi diff --git a/settings.gradle.kts b/settings.gradle.kts index 2bf4013..406ade4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,4 +5,4 @@ include("idscp2-core") include("idscp2-examples") include("idscp2-app-layer") include("idscp2-daps-aisec") -include("camel-idscp2") +include("camel-idscp2") \ No newline at end of file