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