You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@teaclave.apache.org by zf...@apache.org on 2020/05/13 04:51:51 UTC

[incubator-teaclave] branch master updated: Implement an example in Python (builtin echo) (#292)

This is an automated email from the ASF dual-hosted git repository.

zfc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave.git


The following commit(s) were added to refs/heads/master by this push:
     new 25dcc6c  Implement an example in Python (builtin echo) (#292)
25dcc6c is described below

commit 25dcc6c0df6c274562c60655e3dc98e3f303cce8
Author: Mingshen Sun <bo...@mssun.me>
AuthorDate: Tue May 12 21:51:43 2020 -0700

    Implement an example in Python (builtin echo) (#292)
    
    * [tests] Use python3's BaseHTTPRequestHandler
    
    * [examples] Add a builtin echo example written in python
---
 cmake/UtilTargets.cmake             |   4 +-
 cmake/scripts/test.sh               |  34 ++++-
 examples/python/builtin_echo.py     | 270 ++++++++++++++++++++++++++++++++++++
 tests/scripts/simple_http_server.py |  17 ++-
 4 files changed, 320 insertions(+), 5 deletions(-)

diff --git a/cmake/UtilTargets.cmake b/cmake/UtilTargets.cmake
index 7ef35e9..e53b32e 100644
--- a/cmake/UtilTargets.cmake
+++ b/cmake/UtilTargets.cmake
@@ -38,9 +38,11 @@ if(TEST_MODE)
   add_custom_target(
     run-functional-tests COMMAND ${TEACLAVE_COMMON_ENVS}
                                  ${MT_SCRIPT_DIR}/test.sh functional)
+  add_custom_target(run-examples COMMAND ${TEACLAVE_COMMON_ENVS}
+                                 ${MT_SCRIPT_DIR}/test.sh example)
 else()
   add_custom_target(
-    run-test
+    run-tests
     COMMAND
       echo
       "Note: Testing is not enabled in this build. Run cmake again with -DTEST_MODE=ON"
diff --git a/cmake/scripts/test.sh b/cmake/scripts/test.sh
index 87ce604..a7840cf 100755
--- a/cmake/scripts/test.sh
+++ b/cmake/scripts/test.sh
@@ -23,7 +23,7 @@ echo_title() {
 }
 
 start_storage_server() {
-  python ${TEACLAVE_PROJECT_ROOT}/tests/scripts/simple_http_server.py 6789 &
+  python3 ${TEACLAVE_PROJECT_ROOT}/tests/scripts/simple_http_server.py 6789 &
 }
 
 run_unit_tests() {
@@ -133,6 +133,34 @@ run_functional_tests() {
   cleanup
 }
 
+run_examples() {
+  trap cleanup INT TERM ERR
+
+  echo_title "examples"
+  pushd ${TEACLAVE_SERVICE_INSTALL_DIR}
+  ./teaclave_authentication_service &
+  ./teaclave_storage_service &
+  sleep 3    # wait for authentication and storage service
+  ./teaclave_management_service &
+  ./teaclave_scheduler_service &
+  sleep 3    # wait for management service and scheduler_service
+  ./teaclave_access_control_service &
+  ./teaclave_frontend_service &
+  sleep 3    # wait for other services
+
+  start_storage_server
+
+  # Run tests of execution service separately
+  ./teaclave_execution_service &
+  sleep 3    # wait for execution services
+  popd
+
+  python3 ${TEACLAVE_PROJECT_ROOT}/examples/python/builtin_echo.py
+
+  # kill all background services
+  cleanup
+}
+
 case "$1" in
     "unit")
         run_unit_tests
@@ -143,9 +171,13 @@ case "$1" in
     "functional")
         run_functional_tests
         ;;
+    "example")
+        run_examples
+        ;;
     *)
         run_unit_tests
         run_integration_tests
         run_functional_tests
+        run_examples
         ;;
 esac
diff --git a/examples/python/builtin_echo.py b/examples/python/builtin_echo.py
new file mode 100644
index 0000000..7056bce
--- /dev/null
+++ b/examples/python/builtin_echo.py
@@ -0,0 +1,270 @@
+#!/usr/bin/env python3
+
+import socket
+import struct
+import ssl
+import json
+import base64
+import toml
+import os
+import time
+import sys
+
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+
+from OpenSSL.crypto import load_certificate, FILETYPE_PEM, FILETYPE_ASN1
+from OpenSSL.crypto import X509Store, X509StoreContext
+from OpenSSL import crypto
+
+HOSTNAME = 'localhost'
+AUTHENTICATION_SERVICE_ADDRESS = (HOSTNAME, 7776)
+FRONTEND_SERVICE_ADDRESS = (HOSTNAME, 7777)
+CONTEXT = ssl._create_unverified_context()
+
+USER_ID = "example_user"
+USER_PASSWORD = "test_password"
+
+if os.environ.get('DCAP'):
+    AS_ROOT_CERT_FILENAME = "dcap_root_ca_cert.pem"
+else:
+    AS_ROOT_CERT_FILENAME = "ias_root_ca_cert.pem"
+
+if os.environ.get('TEACLAVE_PROJECT_ROOT'):
+    AS_ROOT_CA_CERT_PATH = os.environ['TEACLAVE_PROJECT_ROOT'] + \
+        "/keys/" + AS_ROOT_CERT_FILENAME
+    ENCLAVE_INFO_PATH = os.environ['TEACLAVE_PROJECT_ROOT'] + \
+        "/release/tests/enclave_info.toml"
+else:
+    AS_ROOT_CA_CERT_PATH = "../../keys/" + AS_ROOT_CERT_FILENAME
+    ENCLAVE_INFO_PATH = "../../release/tests/enclave_info.toml"
+
+
+def write_message(sock, message):
+    message = json.dumps(message)
+    message = message.encode()
+    sock.write(struct.pack(">Q", len(message)))
+    sock.write(message)
+
+
+def read_message(sock):
+    response_len = struct.unpack(">Q", sock.read(8))
+    response = sock.read(response_len[0])
+    response = json.loads(response)
+    return response
+
+
+def verify_report(cert, endpoint_name):
+    if os.environ.get('SGX_MODE') == 'SW':
+        return
+
+    cert = x509.load_der_x509_certificate(cert, default_backend())
+    ext = json.loads(cert.extensions[0].value.value)
+
+    report = bytes(ext["report"])
+    signature = bytes(ext["signature"])
+    signing_cert = bytes(ext["signing_cert"])
+    signing_cert = load_certificate(FILETYPE_ASN1, signing_cert)
+
+    # verify signing cert with AS root cert
+    with open(AS_ROOT_CA_CERT_PATH) as f:
+        as_root_ca_cert = f.read()
+    as_root_ca_cert = load_certificate(FILETYPE_PEM, as_root_ca_cert)
+    store = X509Store()
+    store.add_cert(as_root_ca_cert)
+    store.add_cert(signing_cert)
+    store_ctx = X509StoreContext(store, as_root_ca_cert)
+    store_ctx.verify_certificate()
+
+    # verify report's signature
+    crypto.verify(signing_cert, signature, bytes(ext["report"]), 'sha256')
+
+    report = json.loads(report)
+    quote = report['isvEnclaveQuoteBody']
+    quote = base64.b64decode(quote)
+
+    # get mr_enclave and mr_signer from the quote
+    mr_enclave = quote[112:112+32].hex()
+    mr_signer = quote[176:176+32].hex()
+
+    # get enclave_info
+    enclave_info = toml.load(ENCLAVE_INFO_PATH)
+
+    # verify mr_enclave and mr_signer
+    enclave_name = "teaclave_" + endpoint_name + "_service"
+    if mr_enclave != enclave_info[enclave_name]["mr_enclave"]:
+        raise Exception("mr_enclave error")
+
+    if mr_signer != enclave_info[enclave_name]["mr_signer"]:
+        raise Exception("mr_signer error")
+
+
+def user_register(channel, user_id, user_password):
+    message = {
+        "request": "user_register",
+        "id": user_id,
+        "password": user_password
+    }
+    write_message(channel, message)
+    read_message(channel)
+
+
+def user_login(channel, user_id, user_password):
+    message = {
+        "request": "user_login",
+        "id": user_id,
+        "password": user_password
+    }
+    write_message(channel, message)
+
+    response = read_message(channel)
+    assert(response["result"] == "ok")
+    return response["content"]["token"]
+
+
+def register_function(channel, user_id, token):
+    message = {
+        "metadata": {
+            "id": user_id,
+            "token": token
+        },
+        "request": "register_function",
+        "name": "builtin-echo",
+        "description": "Native Echo Function",
+        "executor_type": "builtin",
+        "public": True,
+        "payload": [],
+        "arguments": ["message"],
+        "inputs": [],
+        "outputs": [],
+    }
+    write_message(channel, message)
+
+    response = read_message(channel)
+    return response["content"]["function_id"]
+
+
+def create_task(channel, user_id, token, function_id, message):
+    message = {
+        "metadata": {
+            "id": user_id,
+            "token": token
+        },
+        "request": "create_task",
+        "function_id": function_id,
+        "function_arguments": {
+            "message": message,
+        },
+        "executor": "builtin",
+        "inputs_ownership": [],
+        "outputs_ownership": [],
+    }
+    write_message(channel, message)
+
+    response = read_message(channel)
+    return response["content"]["task_id"]
+
+
+def approve_task(channel, user_id, token, task_id):
+    message = {
+        "metadata": {
+            "id": user_id,
+            "token": token
+        },
+        "request": "approve_task",
+        "task_id": task_id,
+    }
+    write_message(channel, message)
+
+    response = read_message(channel)
+    assert(response["result"] == "ok")
+
+
+def invoke_task(channel, user_id, token, task_id):
+    message = {
+        "metadata": {
+            "id": user_id,
+            "token": token
+        },
+        "request": "invoke_task",
+        "task_id": task_id,
+    }
+    write_message(channel, message)
+
+    response = read_message(channel)
+    assert(response["result"] == "ok")
+
+
+def get_task_result(channel, user_id, token, task_id):
+    message = {
+        "metadata": {
+            "id": user_id,
+            "token": token
+        },
+        "request": "get_task",
+        "task_id": task_id,
+    }
+    while True:
+        write_message(channel, message)
+        response = read_message(channel)
+        time.sleep(1)
+        if response["content"]["status"] == 10:
+            break
+
+    return response["content"]["result"]["result"]["Ok"]["return_value"]
+
+
+class BuiltinEchoExample:
+    def __init__(self, user_id, user_password):
+        self.user_id = user_id
+        self.user_password = user_password
+
+    def echo(self, message="Hello, Teaclave!"):
+        sock = socket.create_connection(AUTHENTICATION_SERVICE_ADDRESS)
+        channel = CONTEXT.wrap_socket(sock, server_hostname=HOSTNAME)
+        cert = channel.getpeercert(binary_form=True)
+        verify_report(cert, "authentication")
+
+        print("[+] registering user")
+        user_register(channel, self.user_id, self.user_password)
+
+        print("[+] login")
+        token = user_login(channel, self.user_id, self.user_password)
+
+        sock = socket.create_connection(FRONTEND_SERVICE_ADDRESS)
+        channel = CONTEXT.wrap_socket(sock, server_hostname=HOSTNAME)
+        cert = channel.getpeercert(binary_form=True)
+        verify_report(cert, "frontend")
+
+        print("[+] registering function")
+        function_id = register_function(channel, self.user_id, token)
+
+        print("[+] creating task")
+        task_id = create_task(channel, self.user_id,
+                              token, function_id, message)
+        print("[+] approving task")
+        approve_task(channel, self.user_id, token, task_id)
+
+        print("[+] invoking task")
+        invoke_task(channel, self.user_id, token, task_id)
+
+        print("[+] getting result")
+        result = get_task_result(channel, self.user_id, token, task_id)
+        print("[+] done")
+
+        return bytes(result)
+
+
+def main():
+    example = BuiltinEchoExample(USER_ID, USER_PASSWORD)
+    if len(sys.argv) > 1:
+        message = sys.argv[1]
+        rt = example.echo(message)
+    else:
+        rt = example.echo()
+
+    print("[+] function return: ", rt)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/scripts/simple_http_server.py b/tests/scripts/simple_http_server.py
index e3386c5..3fe4aa4 100644
--- a/tests/scripts/simple_http_server.py
+++ b/tests/scripts/simple_http_server.py
@@ -1,7 +1,11 @@
-import SimpleHTTPServer
+#!/usr/bin/env python3
 
+from http.server import SimpleHTTPRequestHandler
+import socketserver
+import sys
 
-class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+class HTTPRequestHandler(SimpleHTTPRequestHandler):
     def do_PUT(self):
         length = int(self.headers["Content-Length"])
 
@@ -14,4 +18,11 @@ class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
 
 
 if __name__ == '__main__':
-    SimpleHTTPServer.test(HandlerClass=HTTPRequestHandler)
+    if len(sys.argv) > 1:
+        port = int(sys.argv[1])
+    else:
+        port = 6789
+    socketserver.TCPServer.allow_reuse_address = True
+    with socketserver.TCPServer(("localhost", port), HTTPRequestHandler) as httpd:
+        print("serving at port", port)
+        httpd.serve_forever()


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org