You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@teaclave.apache.org by sh...@apache.org on 2022/11/11 05:17:46 UTC
[incubator-teaclave-java-tee-sdk] 06/48: [sdk] Add enclave remote attestation api for JavaEnclave
This is an automated email from the ASF dual-hosted git repository.
shaojunwang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-java-tee-sdk.git
commit bf26f1d3e6e7a36bb940fb52f3050f97e51db80e
Author: jeffery.wsj <je...@alibaba-inc.com>
AuthorDate: Tue Feb 22 21:10:50 2022 +0800
[sdk] Add enclave remote attestation api for JavaEnclave
Summary: Add two enclave remote attestation api, one api is enclave report generation,
another api is enclave report verification.
Test Plan: all tests pass
Reviewers: lei.yul, cengfeng.lzy, sanhong.lsh
Issue: https://aone.alibaba-inc.com/task/39713494
CR: https://code.aone.alibaba-inc.com/java-tee/JavaEnclave/codereview/7817874
---
.../host/AbstractEnclave.java | 4 ++
.../host/AttestationReport.java | 78 ++++++++++++++++++++++
.../confidentialcomputing/host/Enclave.java | 6 +-
.../confidentialcomputing/host/ExtractLibrary.java | 6 +-
.../host/MockInJvmEnclave.java | 11 +++
.../host/MockInSvmEnclave.java | 10 +++
.../host/RemoteAttestation.java | 62 +++++++++++++++++
.../confidentialcomputing/host/TeeSdkEnclave.java | 28 +++++++-
.../exception/ConfidentialComputingException.java | 4 +-
.../host/exception/RemoteAttestationException.java | 30 +++++++++
.../host/MockTestEnclave.java | 10 +++
.../host/TestRemoteAttestation.java | 53 +++++++++++++++
12 files changed, 298 insertions(+), 4 deletions(-)
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/AbstractEnclave.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/AbstractEnclave.java
index 37c87a9..50a92f6 100644
--- a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/AbstractEnclave.java
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/AbstractEnclave.java
@@ -13,9 +13,11 @@ import com.alibaba.confidentialcomputing.common.SerializationHelper;
import com.alibaba.confidentialcomputing.common.ServiceHandler;
import com.alibaba.confidentialcomputing.host.exception.EnclaveCreatingException;
import com.alibaba.confidentialcomputing.host.exception.EnclaveMethodInvokingException;
+import com.alibaba.confidentialcomputing.host.exception.RemoteAttestationException;
import com.alibaba.confidentialcomputing.host.exception.ServicesLoadingException;
import com.alibaba.confidentialcomputing.host.exception.ServicesUnloadingException;
+
/**
* AbstractEnclave implements all kinds of enclave platform's common operation.
* Such as service loadingăunloading and service method invocation.
@@ -163,6 +165,8 @@ abstract class AbstractEnclave implements Enclave {
}
}
+ abstract AttestationReport generateAttestationReport(byte[] userData) throws RemoteAttestationException;
+
@Override
public <T> Iterator<T> load(Class<T> service) throws ServicesLoadingException {
// Check service must be an interface class.
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/AttestationReport.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/AttestationReport.java
new file mode 100644
index 0000000..4dd574d
--- /dev/null
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/AttestationReport.java
@@ -0,0 +1,78 @@
+package com.alibaba.confidentialcomputing.host;
+
+import java.io.Serializable;
+
+/**
+ * AttestationReport wraps enclave's type and generated remote attestation report.
+ */
+public final class AttestationReport implements Serializable {
+ private static final long serialVersionUID = -2781780414647128479L;
+
+ private final EnclaveType enclaveType;
+ private final byte[] report;
+
+ AttestationReport(EnclaveType enclaveType, byte[] report) {
+ this.enclaveType = enclaveType;
+ this.report = report;
+ }
+
+ /**
+ * Get enclave type from an AttestationReport instance.
+ * <p>
+ *
+ * @return Enclave type.
+ */
+ public EnclaveType getEnclaveType() {
+ return enclaveType;
+ }
+
+ /**
+ * Get enclave report from an AttestationReport instance.
+ * <p>
+ *
+ * @return Remote attestation report data.
+ */
+ public byte[] getReport() {
+ return report;
+ }
+
+ /**
+ * Bind an AttestationReport's type and report into a buffer for rpc transmission.
+ * <p>
+ *
+ * @return Serialized buffer.
+ */
+ public byte[] toByteArray() {
+ byte[] bindReport = new byte[1 + report.length];
+ bindReport[0] = (byte) enclaveType.ordinal();
+ System.arraycopy(report, 0, bindReport, 1, report.length);
+ return bindReport;
+ }
+
+ /**
+ * Build an AttestationReport instance from a bind buffer which contains its type and report.
+ * <p>
+ *
+ * @return AttestationReport instance.
+ */
+ public static AttestationReport fromByteArray(byte[] attestationReport) {
+ EnclaveType enclaveType = EnclaveType.NONE;
+ byte[] report = new byte[attestationReport.length - 1];
+ switch (attestationReport[0]) {
+ case 0:
+ enclaveType = EnclaveType.NONE;
+ break;
+ case 1:
+ enclaveType = EnclaveType.MOCK_IN_JVM;
+ break;
+ case 2:
+ enclaveType = EnclaveType.MOCK_IN_SVM;
+ break;
+ case 3:
+ enclaveType = EnclaveType.TEE_SDK;
+ break;
+ }
+ System.arraycopy(attestationReport, 1, report, 0, report.length);
+ return new AttestationReport(enclaveType, report);
+ }
+}
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/Enclave.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/Enclave.java
index 1baf92a..f0a3b52 100644
--- a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/Enclave.java
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/Enclave.java
@@ -42,6 +42,11 @@ import com.alibaba.confidentialcomputing.host.exception.EnclaveDestroyingExcepti
* <pre>
* try {
* Enclave enclave = EnclaveFactory.create();
+ * AttestationReport report = RemoteAttestation.generateAttestationReport(enclave, new byte[64]);
+ * int valid = RemoteAttestation.verifyAttestationReport(report);
+ * if (valid == 0) {
+ * ... ... ...
+ * }
* ... ... ...
* Service provider = enclave.load(Service.class);
* ... ... ...
@@ -55,7 +60,6 @@ import com.alibaba.confidentialcomputing.host.exception.EnclaveDestroyingExcepti
* </pre>
*/
public interface Enclave {
-
/**
* Returns all providers which implement service interface. It's similar to SPI
* ServiceLoader mechanism. It returns proxy providers which are handlers to real
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/ExtractLibrary.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/ExtractLibrary.java
index 84fc41b..1ce65e0 100644
--- a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/ExtractLibrary.java
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/ExtractLibrary.java
@@ -1,6 +1,10 @@
package com.alibaba.confidentialcomputing.host;
-import java.io.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
/**
* JavaEnclave building tool will put native .so files into a java .jar file,
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInJvmEnclave.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInJvmEnclave.java
index faa0a87..030feaf 100644
--- a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInJvmEnclave.java
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInJvmEnclave.java
@@ -1,5 +1,7 @@
package com.alibaba.confidentialcomputing.host;
+import com.alibaba.confidentialcomputing.host.exception.RemoteAttestationException;
+
/**
* MockInJvmEnclave is a mock jvm enclave. Both host and enclave codes run
* in one jvm. It was used for test and debug.
@@ -10,6 +12,15 @@ class MockInJvmEnclave extends AbstractEnclave {
super(EnclaveType.MOCK_IN_JVM, new BaseEnclaveServicesRecycler());
}
+ @Override
+ AttestationReport generateAttestationReport(byte[] userData) throws RemoteAttestationException {
+ throw new RemoteAttestationException("MOCK_IN_JVM enclave doesn't support remote attestation generation.");
+ }
+
+ static int verifyAttestationReport(byte[] report) throws RemoteAttestationException {
+ throw new RemoteAttestationException("MOCK_IN_JVM enclave doesn't support remote attestation verification.");
+ }
+
@Override
InnerNativeInvocationResult loadServiceNative(byte[] payload) {
return null;
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInSvmEnclave.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInSvmEnclave.java
index 84e4618..3b676ea 100644
--- a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInSvmEnclave.java
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/MockInSvmEnclave.java
@@ -2,6 +2,7 @@ package com.alibaba.confidentialcomputing.host;
import com.alibaba.confidentialcomputing.host.exception.EnclaveCreatingException;
import com.alibaba.confidentialcomputing.host.exception.EnclaveDestroyingException;
+import com.alibaba.confidentialcomputing.host.exception.RemoteAttestationException;
import java.io.IOException;
@@ -63,6 +64,15 @@ class MockInSvmEnclave extends AbstractEnclave {
}
}
+ @Override
+ AttestationReport generateAttestationReport(byte[] userData) throws RemoteAttestationException {
+ throw new RemoteAttestationException("MOCK_IN_SVM enclave doesn't support remote attestation generation.");
+ }
+
+ static int verifyAttestationReport(byte[] report) throws RemoteAttestationException {
+ throw new RemoteAttestationException("MOCK_IN_SVM enclave doesn't support remote attestation verification.");
+ }
+
@Override
InnerNativeInvocationResult loadServiceNative(byte[] payload) {
return nativeLoadService(
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/RemoteAttestation.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/RemoteAttestation.java
new file mode 100644
index 0000000..c5bd9df
--- /dev/null
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/RemoteAttestation.java
@@ -0,0 +1,62 @@
+package com.alibaba.confidentialcomputing.host;
+
+import com.alibaba.confidentialcomputing.host.exception.RemoteAttestationException;
+
+import java.util.Random;
+
+/**
+ * RemoteAttestation mainly provides enclave's remote attestation generation and verification.
+ */
+public final class RemoteAttestation {
+ // normalizeUserData format userData to make sure it's a buffer with 64 bytes.
+ private static byte[] normalizeUserData(byte[] userData) throws RemoteAttestationException {
+ byte[] result = null;
+ if (userData == null) {
+ result = new byte[64];
+ new Random().nextBytes(result);
+ } else if (userData.length < 64) {
+ result = new byte[64];
+ System.arraycopy(userData, 0, result, 0, userData.length);
+ } else if (userData.length > 64) {
+ throw new RemoteAttestationException("enclave remote attestation user data length exceeds 64 bytes.");
+ }
+ return result;
+ }
+
+ /**
+ * Generate enclave's remote attestation report, the report is signed by the enclave platform
+ * in TEE, it's used to verify enclave's validation.
+ * <p>
+ *
+ * @param enclave an enclave instance.
+ * @param userData provided as user identification, its length must be 64 bytes.
+ * If userData is null, JavaEnclave will generate a random buffer
+ * with 64 length bytes for it.
+ * If userData's length exceeds 64 bytes, RemoteAttestationException
+ * will be thrown.
+ * If userData's length is less than 64 bytes, padding was filled in the tail.
+ * @return Remote attestation report data.
+ * @throws RemoteAttestationException {@link RemoteAttestationException} If enclave remote
+ * attestation generated failed.
+ */
+ public static AttestationReport generateAttestationReport(Enclave enclave, byte[] userData) throws RemoteAttestationException {
+ if (!(enclave instanceof AbstractEnclave)) {
+ throw new RemoteAttestationException("enclave instance class type is not AbstractEnclave.");
+ }
+ return ((AbstractEnclave) enclave).generateAttestationReport(normalizeUserData(userData));
+ }
+
+ /**
+ * Verify an enclave's validation according to giving report data signed by one enclave.
+ * <p>
+ *
+ * @param report signed data in an enclave and its tee type info.
+ * @return Zero means enclave is valid, Other value means enclave is invalid.
+ */
+ public static int verifyAttestationReport(AttestationReport report) throws RemoteAttestationException {
+ if (report.getEnclaveType() != EnclaveType.TEE_SDK) {
+ throw new RemoteAttestationException("enclaveType must be TEE_SDK.");
+ }
+ return TeeSdkEnclave.verifyAttestationReport(report.getReport());
+ }
+}
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/TeeSdkEnclave.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/TeeSdkEnclave.java
index e7ee41e..2cd03f7 100644
--- a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/TeeSdkEnclave.java
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/TeeSdkEnclave.java
@@ -1,6 +1,8 @@
package com.alibaba.confidentialcomputing.host;
-import com.alibaba.confidentialcomputing.host.exception.*;
+import com.alibaba.confidentialcomputing.host.exception.EnclaveCreatingException;
+import com.alibaba.confidentialcomputing.host.exception.EnclaveDestroyingException;
+import com.alibaba.confidentialcomputing.host.exception.RemoteAttestationException;
import java.io.IOException;
@@ -53,6 +55,10 @@ class TeeSdkEnclave extends AbstractEnclave {
private native int nativeCreateEnclave(int mode, String path);
+ private native InnerNativeInvocationResult nativeGenerateAttestationReport(byte[] userData);
+
+ private static native InnerNativeInvocationResult nativeVerifyAttestationReport(byte[] report);
+
private native int nativeSvmAttachIsolate(long enclaveHandler);
private native InnerNativeInvocationResult nativeLoadService(
@@ -68,6 +74,26 @@ class TeeSdkEnclave extends AbstractEnclave {
private native int nativeDestroyEnclave(long enclaveHandler);
+ @Override
+ AttestationReport generateAttestationReport(byte[] userData) throws RemoteAttestationException {
+ InnerNativeInvocationResult result = nativeGenerateAttestationReport(userData);
+ if (result.getRet() != 0) {
+ throw new RemoteAttestationException("TEE_SDK's attestation report generation native call error code: " + result.getRet());
+ }
+ return new AttestationReport(EnclaveType.TEE_SDK, result.getPayload());
+ }
+
+ static int verifyAttestationReport(byte[] report) throws RemoteAttestationException {
+ InnerNativeInvocationResult result = nativeVerifyAttestationReport(report);
+ if (result.getRet() != 0) {
+ throw new RemoteAttestationException("TEE_SDK's attestation verification native call error code: " + result.getRet());
+ }
+ if (result.getPayload() == null) {
+ return 0; // Remote Attestation Verification result is succeed.
+ }
+ return 1; // Remote Attestation Verification result is failed.
+ }
+
@Override
InnerNativeInvocationResult loadServiceNative(byte[] payload) {
return nativeLoadService(
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/exception/ConfidentialComputingException.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/exception/ConfidentialComputingException.java
index f7af01a..85ab3fe 100644
--- a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/exception/ConfidentialComputingException.java
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/exception/ConfidentialComputingException.java
@@ -49,7 +49,9 @@ public class ConfidentialComputingException extends Exception {
// Services unloading failed.
SERVICES_UNLOADING_ERROR("A0004", "service unloading failed in enclave"),
// Service method invoking failed.
- SERVICE_METHOD_INVOKING_ERROR("A0005", "service method invoking failed");
+ SERVICE_METHOD_INVOKING_ERROR("A0005", "service method invoking failed"),
+ // Enclave remote attestation exception.
+ ENCLAVE_REMOTE_ATTESTATION_ERROR("A0006", "tee remote attestation failed");
private final String errorCode;
private final String errorMessage;
diff --git a/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/exception/RemoteAttestationException.java b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/exception/RemoteAttestationException.java
new file mode 100644
index 0000000..d5faaab
--- /dev/null
+++ b/sdk/host/src/main/java/com/alibaba/confidentialcomputing/host/exception/RemoteAttestationException.java
@@ -0,0 +1,30 @@
+package com.alibaba.confidentialcomputing.host.exception;
+
+/**
+ * RemoteAttestationException {@link RemoteAttestationException} is thrown when an enclave generates remote
+ * attestation report and returns an error value.
+ * Programmers need to handle RemoteAttestationException seriously.
+ */
+public class RemoteAttestationException extends ConfidentialComputingException {
+ /**
+ * @param info exception information.
+ */
+ public RemoteAttestationException(String info) {
+ super(EnclaveNativeInvokingException.ENCLAVE_REMOTE_ATTESTATION_ERROR.buildExceptionMessage(info));
+ }
+
+ /**
+ * @param e exception.
+ */
+ public RemoteAttestationException(Throwable e) {
+ super(EnclaveNativeInvokingException.ENCLAVE_REMOTE_ATTESTATION_ERROR.toString(), e);
+ }
+
+ /**
+ * @param info exception message.
+ * @param e exception.
+ */
+ public RemoteAttestationException(String info, Throwable e) {
+ super(EnclaveNativeInvokingException.ENCLAVE_REMOTE_ATTESTATION_ERROR.buildExceptionMessage(info), e);
+ }
+}
diff --git a/sdk/host/src/test/java/com/alibaba/confidentialcomputing/host/MockTestEnclave.java b/sdk/host/src/test/java/com/alibaba/confidentialcomputing/host/MockTestEnclave.java
index e1a9b34..04faf7b 100644
--- a/sdk/host/src/test/java/com/alibaba/confidentialcomputing/host/MockTestEnclave.java
+++ b/sdk/host/src/test/java/com/alibaba/confidentialcomputing/host/MockTestEnclave.java
@@ -2,6 +2,7 @@ package com.alibaba.confidentialcomputing.host;
import com.alibaba.confidentialcomputing.common.*;
import com.alibaba.confidentialcomputing.host.exception.EnclaveCreatingException;
+import com.alibaba.confidentialcomputing.host.exception.RemoteAttestationException;
import static org.junit.jupiter.api.Assertions.*;
@@ -62,6 +63,15 @@ class MockTestEnclave extends AbstractEnclave {
return Class.forName(name);
}
+ @Override
+ AttestationReport generateAttestationReport(byte[] userData) throws RemoteAttestationException {
+ throw new RemoteAttestationException("MockTestEnclave enclave doesn't support remote attestation generation.");
+ }
+
+ static int verifyAttestationReport(byte[] report) throws RemoteAttestationException {
+ throw new RemoteAttestationException("MockTestEnclave enclave doesn't support remote attestation verification.");
+ }
+
@Override
InnerNativeInvocationResult loadServiceNative(byte[] payload) {
EnclaveInvocationContext invocationContext;
diff --git a/sdk/host/src/test/java/com/alibaba/confidentialcomputing/host/TestRemoteAttestation.java b/sdk/host/src/test/java/com/alibaba/confidentialcomputing/host/TestRemoteAttestation.java
new file mode 100644
index 0000000..4773b0c
--- /dev/null
+++ b/sdk/host/src/test/java/com/alibaba/confidentialcomputing/host/TestRemoteAttestation.java
@@ -0,0 +1,53 @@
+package com.alibaba.confidentialcomputing.host;
+
+import com.alibaba.confidentialcomputing.host.exception.EnclaveCreatingException;
+import com.alibaba.confidentialcomputing.host.exception.RemoteAttestationException;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class TestRemoteAttestation {
+ @Test
+ void testRemoteAttestation() throws EnclaveCreatingException {
+ Enclave mockInJvmEnclave = new MockInJvmEnclave();
+ assertThrows(RemoteAttestationException.class, () -> RemoteAttestation.generateAttestationReport(mockInJvmEnclave, null));
+ assertThrows(RemoteAttestationException.class, () -> RemoteAttestation.verifyAttestationReport(new AttestationReport(EnclaveType.MOCK_IN_JVM, null)));
+ assertThrows(RemoteAttestationException.class, () -> RemoteAttestation.verifyAttestationReport(new AttestationReport(EnclaveType.MOCK_IN_SVM, null)));
+ }
+
+ @Test
+ void testNormalizeUserData() throws Exception {
+ Class<RemoteAttestation> clazz = RemoteAttestation.class;
+ Method method = clazz.getDeclaredMethod("normalizeUserData", byte[].class);
+ method.setAccessible(true);
+
+ byte[] parameter = null;
+ Object result = method.invoke(null, (Object) parameter);
+ assertEquals(((byte[]) result).length, 64);
+
+ parameter = new byte[32];
+ result = method.invoke(null, parameter);
+ assertEquals(((byte[]) result).length, 64);
+
+ byte[] finalParameter = new byte[65];
+ assertThrows(InvocationTargetException.class, () -> method.invoke(null, finalParameter));
+ }
+
+ @Test
+ void testAttestationReport() throws Exception {
+ byte[] quote = new byte[4];
+ for (int index = 0; index < quote.length; index++) {
+ quote[index] = (byte) 0x5f;
+ }
+ AttestationReport report = new AttestationReport(EnclaveType.TEE_SDK, quote);
+ byte[] serializedReport = report.toByteArray();
+ AttestationReport deserializedReport = AttestationReport.fromByteArray(serializedReport);
+ assertEquals(EnclaveType.TEE_SDK, deserializedReport.getEnclaveType());
+ for (int index = 0; index < quote.length; index++) {
+ assertEquals(quote[index], (deserializedReport.getReport())[index]);
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org