You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesatee.apache.org by ur...@apache.org on 2019/11/20 02:39:20 UTC
[incubator-mesatee] 01/01: WIP: access control service
This is an automated email from the ASF dual-hosted git repository.
uraj pushed a commit to branch acs
in repository https://gitbox.apache.org/repos/asf/incubator-mesatee.git
commit fe6344674fe659b9795b4d20fa919fd8a0492fa0
Author: Pei Wang <wa...@baidu.com>
AuthorDate: Tue Oct 15 16:22:03 2019 -0700
WIP: access control service
---
.gitmodules | 4 +
CMakeLists.txt | 2 +-
Makefile.deprecated | 23 +-
config.toml | 1 +
mesatee_config/src/runtime_config.rs | 11 +
mesatee_core/src/config/internal.rs | 16 +
mesatee_core/src/error.rs | 7 +-
mesatee_services/acs/client/Cargo.toml | 22 +
mesatee_services/acs/client/src/acs_client.rs | 150 +++++
.../acs/client/src/lib.rs | 12 +-
mesatee_services/acs/model.conf | 32 ++
mesatee_services/acs/proto/Cargo.toml | 23 +
.../acs/proto/src/lib.rs | 11 +-
mesatee_services/acs/proto/src/proto.rs | 75 +++
mesatee_services/acs/python/acs_engine.py | 631 +++++++++++++++++++++
mesatee_services/acs/python/acs_engine_test.py | 82 +++
mesatee_services/acs/python/ffi.py | 15 +
mesatee_services/acs/sgx_app/Cargo.toml | 23 +
mesatee_services/acs/sgx_app/build.rs | 61 ++
mesatee_services/acs/sgx_app/src/main.rs | 86 +++
mesatee_services/acs/sgx_trusted_lib/Cargo.toml | 30 +
.../acs/sgx_trusted_lib/Enclave.config.xml | 12 +
mesatee_services/acs/sgx_trusted_lib/src/acs.rs | 231 ++++++++
.../acs/sgx_trusted_lib/src/lib.rs | 21 +-
mesatee_services/acs/sgx_trusted_lib/src/sgx.rs | 106 ++++
tests/functional_test.sh | 10 +-
tests/functional_test/sgx_trusted_lib/Cargo.toml | 3 +-
tests/functional_test/sgx_trusted_lib/src/sgx.rs | 1 +
.../sgx_trusted_lib/src/tests/acs_test.rs | 267 +++++++++
.../sgx_trusted_lib/src/tests/common_setup.rs | 7 +
.../sgx_trusted_lib/src/tests/mod.rs | 1 +
third_party/mesapy | 1 +
toolchain_deps/Cargo.sgx_trusted_lib.toml | 1 +
toolchain_deps/Cargo.sgx_untrusted_app.toml | 1 +
34 files changed, 1952 insertions(+), 27 deletions(-)
diff --git a/.gitmodules b/.gitmodules
index e7c30ff..d8d15b1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,7 @@
[submodule "third_party/rust-sgx-sdk"]
path = third_party/rust-sgx-sdk
url = https://github.com/baidu/rust-sgx-sdk.git
+[submodule "third_party/mesapy"]
+ path = third_party/mesapy
+ url = https://github.com/mesalock-linux/mesapy.git
+ branch = sgx
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2ed4978..eb0b460 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,7 +20,7 @@ option(COV OFF "Turn on coverage or not")
set(UNIX_LIBS mesatee_sdk protected_fs_rs)
# need relative path for SGX_MODULE_PATHS to find Enclave.config.xml
set(SGX_MODULE_PATHS mesatee_services/kms mesatee_services/tdfs mesatee_services/tms
- mesatee_services/fns tests/functional_test)
+ mesatee_services/fns mesatee_services/acs tests/functional_test)
# SGX_MODULES is generated from SGX_MODULE_PATHS
# set(SGX_MODULES kms tdfs tms fns functional_test)
# ================ VARIABLES FOR MANUAL CHANGE END ================
diff --git a/Makefile.deprecated b/Makefile.deprecated
index d6d0271..006f354 100644
--- a/Makefile.deprecated
+++ b/Makefile.deprecated
@@ -72,7 +72,7 @@ TEE_BINDER_DIR := $(MODULES_DIR)/mesatee_binder
EDL_FILE := $(TEE_BINDER_DIR)/Enclave.edl
SGX_MODULES := mesatee_services/kms mesatee_services/tdfs mesatee_services/tms \
- mesatee_services/fns tests/functional_test
+ mesatee_services/fns mesatee_services/acs tests/functional_test
SGX_LIBS :=
UNIX_MODULES := integration_test private_join_and_compute quickstart \
image_resizing online_decrypt rsa_sign py_matrix_multiply py_file py_logistic_reg kmeans \
@@ -182,14 +182,14 @@ sgx_build_clean:
#arg1: module name
#arg2: enclave config
define sgx_link
- cd $(OUT_DIR) && $(CC) libEnclave_t.o ffi.o -o \
+ cd $(OUT_DIR) && $(CC) libEnclave_t.o -o \
$(OUT_DIR)/$(strip $(1)).enclave.so $(SGX_COMMON_CFLAGS) \
-Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles \
-L$(SGX_LIBRARY_PATH) -Wl,--whole-archive -l$(Trts_Library_Name) \
-Wl,--no-whole-archive -Wl,--start-group \
-l$(Service_Library_Name) -lsgx_tprotected_fs -lsgx_tkey_exchange\
-lsgx_tstdc -lsgx_tcxx -lsgx_tservice -lsgx_tcrypto \
- -L$(OUT_DIR) -lpypy-c -lsgx_tlibc_ext -lffi \
+ -L$(OUT_DIR) -lpycomponent ffi.o -lpypy-c -lsgx_tlibc_ext -lffi \
-L$(TRUSTED_TARGET_DIR)/$(TARGET) -l$(1)_enclave -Wl,--end-group \
-Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \
-Wl,-pie,-eenclave_entry -Wl,--export-dynamic \
@@ -225,7 +225,7 @@ sgx_pregen: prep $(EDL_FILE) $(SGX_EDGER8R)
--search-path $(RUST_SGX_SDK)/edl --trusted-dir $(OUT_DIR)
cd $(OUT_DIR) && $(CC) $(SGX_TRUSTED_CFLAGS) -c Enclave_t.c -o libEnclave_t.o
-$(SGX_MODULES): config_gen sgx_pregen
+$(SGX_MODULES): config_gen sgx_pregen pycomponent
echo -e "$(BOLD)[*] Building $@$(END_BOLD)"
$(call cargo_toml_prepare, sgx_untrusted_app)
$(call cargo_build, $(MODULES_DIR), $(UNTRUSTED_TARGET_DIR), -p $(notdir $@))
@@ -238,6 +238,17 @@ $(SGX_MODULES): config_gen sgx_pregen
$(call sgx_build_clean)
$(call sgx_link, $(notdir $@), $@/sgx_trusted_lib/Enclave.config.xml)
+pycomponent: $(OUT_DIR)/libpycomponent.a
+
+$(OUT_DIR)/libpycomponent.a: $(OUT_DIR)/acs_py_enclave.o
+ cd $(OUT_DIR) && ar rcs $@ $?
+
+$(OUT_DIR)/acs_py_enclave.c: mesatee_services/acs/python/ffi.py mesatee_services/acs/python/acs_engine.py prep
+ env PYTHONPATH=$(THIRD_PARTY_DIR)/mesapy/sgx PYPY_FFI_OUTDIR=$(OUT_DIR) pypy $<
+
+$(OUT_DIR)/acs_py_enclave.o: $(OUT_DIR)/acs_py_enclave.c
+ $(CC) -O2 -UWITH_THREAD -DSGX -fPIC -Wimplicit -I/usr/lib/pypy/include $< -c -o $@
+
sgx_untrusted: config_gen sgx_pregen
echo -e "$(BOLD)[*] Building $@$(END_BOLD)"
$(call cargo_toml_prepare, sgx_untrusted_app)
@@ -245,7 +256,7 @@ sgx_untrusted: config_gen sgx_pregen
$(call cargo_toml_clean)
for m in $(SGX_MODULES); do cp $(UNTRUSTED_TARGET_DIR)/$(TARGET)/$${m##*/} $(MESATEE_BIN_DIR); done
-sgx_trusted: config_gen sgx_pregen sgx_untrusted
+sgx_trusted: config_gen sgx_pregen sgx_untrusted pycomponent
$(call sgx_build_prepare)
for m in $(SGX_MODULES); do \
echo -e "$(BOLD)[*] Building $${m}_enclave$(END_BOLD)" && \
@@ -293,6 +304,7 @@ fns: mesatee_services/fns
kms: mesatee_services/kms
tms: mesatee_services/tms
tdfs: mesatee_services/tdfs
+acs: mesatee_services/acs
sgx: sgx_trusted sgx_untrusted
@@ -304,6 +316,7 @@ cov:
find . \( -name "*.gcda" -and \( ! -name "sgx_cov*" \
-and ! -name "kms*" -and ! -name "fns*" \
-and ! -name "tdfs*" -and ! -name "tms*" \
+ -and ! -name "acs*" \
-and ! -name "private_join_and_compute*"\
-and ! -name "online_decrypt*"\
-and ! -name "image_resizing*"\
diff --git a/config.toml b/config.toml
index 7c10c74..552dfb4 100644
--- a/config.toml
+++ b/config.toml
@@ -48,6 +48,7 @@ fns = { listen_ip = "0.0.0.0", connect_ip = "127.0.0.1", port = 3444 }
tms = { listen_ip = "0.0.0.0", connect_ip = "127.0.0.1", port = 5555 }
tdfs = { listen_ip = "0.0.0.0", connect_ip = "127.0.0.1", port = 5066 }
kms = { listen_ip = "0.0.0.0", connect_ip = "127.0.0.1", port = 6016 }
+acs = { listen_ip = "0.0.0.0", connect_ip = "127.0.0.1", port = 5077 }
# This section configures the location of certificate/private key used to
diff --git a/mesatee_config/src/runtime_config.rs b/mesatee_config/src/runtime_config.rs
index d8a6274..6ef8c80 100644
--- a/mesatee_config/src/runtime_config.rs
+++ b/mesatee_config/src/runtime_config.rs
@@ -91,6 +91,9 @@ impl MesateeConfigToml {
if !internal_endpoints.contains_key("kms") {
return Err(err("[api_endpoint]: missing `kms`"));
}
+ if !internal_endpoints.contains_key("acs") {
+ return Err(err("[api_endpoint]: missing `acs`"));
+ }
let audited_enclave_config = &self.audited_enclave_config;
if !audited_enclave_config.contains_key("enclave_info") {
@@ -165,6 +168,10 @@ pub struct MesateeConfig {
pub kms_internal_connect_addr: IpAddr,
pub kms_internal_port: u16,
+ pub acs_internal_listen_addr: IpAddr,
+ pub acs_internal_connect_addr: IpAddr,
+ pub acs_internal_port: u16,
+
pub fns_external_listen_addr: IpAddr,
pub fns_external_connect_addr: IpAddr, // for TMS to return to users
pub fns_external_port: u16,
@@ -220,6 +227,10 @@ lazy_static! {
kms_internal_connect_addr: MESATEE_CONFIG_TOML.internal_endpoints["kms"].connect_ip,
kms_internal_port: MESATEE_CONFIG_TOML.internal_endpoints["kms"].port,
+ acs_internal_listen_addr: MESATEE_CONFIG_TOML.internal_endpoints["acs"].listen_ip,
+ acs_internal_connect_addr: MESATEE_CONFIG_TOML.internal_endpoints["acs"].connect_ip,
+ acs_internal_port: MESATEE_CONFIG_TOML.internal_endpoints["acs"].port,
+
fns_external_listen_addr: MESATEE_CONFIG_TOML.api_endpoints["fns"].listen_ip,
// `connect_ip` is a required field for FNS in [api_endpoints];
// we can unwrap() safely because we have checked it in MesateeConfigToml::is_valid().
diff --git a/mesatee_core/src/config/internal.rs b/mesatee_core/src/config/internal.rs
index 82b2b4a..e350511 100644
--- a/mesatee_core/src/config/internal.rs
+++ b/mesatee_core/src/config/internal.rs
@@ -45,6 +45,14 @@ impl Internal {
)
}
+ pub fn acs() -> ServiceConfig {
+ ServiceConfig::new(
+ MESATEE_CONFIG.acs_internal_listen_addr,
+ MESATEE_CONFIG.acs_internal_port,
+ InboundDesc::Sgx(get_trusted_enclave_attr(vec!["kms", "tms", "tdfs"])),
+ )
+ }
+
pub fn target_tms() -> TargetDesc {
TargetDesc::new(
MESATEE_CONFIG.tms_internal_connect_addr,
@@ -68,4 +76,12 @@ impl Internal {
OutboundDesc::Sgx(get_trusted_enclave_attr(vec!["tdfs"])),
)
}
+
+ pub fn target_acs() -> TargetDesc {
+ TargetDesc::new(
+ MESATEE_CONFIG.acs_internal_connect_addr,
+ MESATEE_CONFIG.acs_internal_port,
+ OutboundDesc::Sgx(get_trusted_enclave_attr(vec!["acs"])),
+ )
+ }
}
diff --git a/mesatee_core/src/error.rs b/mesatee_core/src/error.rs
index 84655b6..f54a6a5 100644
--- a/mesatee_core/src/error.rs
+++ b/mesatee_core/src/error.rs
@@ -113,10 +113,12 @@ pub enum ErrorKind {
IPCError,
/// IAS client key or cert not available
IASClientKeyCertError,
- /// No valid worker for the task,
+ /// No valid worker for the task
NoValidWorkerError,
/// RPC Message size excceds the limit
MsgSizeLimitExceedError,
+ /// Unhandled MesaPy exception encountered
+ MesaPyError,
/// Others.
Unknown,
}
@@ -189,6 +191,7 @@ impl ErrorKind {
}
ErrorKind::NoValidWorkerError => "no valid worker error",
ErrorKind::MsgSizeLimitExceedError => "message size exceeds limit",
+ ErrorKind::MesaPyError => "unhandled mesapy exception",
ErrorKind::Unknown => "unknown error",
}
}
@@ -240,6 +243,7 @@ impl From<u32> for Error {
0x0000_1011 => ErrorKind::IASClientKeyCertError,
0x0000_1012 => ErrorKind::NoValidWorkerError,
0x0000_1013 => ErrorKind::MsgSizeLimitExceedError,
+ 0x0000_1014 => ErrorKind::MesaPyError,
_ => ErrorKind::Unknown,
};
@@ -287,6 +291,7 @@ impl Into<u32> for Error {
ErrorKind::IASClientKeyCertError => 0x0000_1011,
ErrorKind::NoValidWorkerError => 0x0000_1012,
ErrorKind::MsgSizeLimitExceedError => 0x0000_1013,
+ ErrorKind::MesaPyError => 0x0000_1014,
ErrorKind::Unknown => 0xffff_ffff,
}
}
diff --git a/mesatee_services/acs/client/Cargo.toml b/mesatee_services/acs/client/Cargo.toml
new file mode 100644
index 0000000..e7fe225
--- /dev/null
+++ b/mesatee_services/acs/client/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "acs_client"
+version = "0.1.0"
+authors = ["MesaTEE Authors <de...@mesatee.org>"]
+description = "RPC client of ACS."
+license = "Apache-2.0"
+edition = "2018"
+
+[features]
+default = []
+mesalock_sgx = ["sgx_tstd", "mesatee_core/mesalock_sgx", "acs_proto/mesalock_sgx"]
+cov = ["sgx_cov"]
+
+[dependencies]
+cfg-if = { version = "0.1.9" }
+acs_proto = { path = "../proto" }
+
+mesatee_core = { version = "0.1.0" }
+
+sgx_types = { version = "1.0.9" }
+sgx_cov = { version = "0.1.0", optional = true }
+sgx_tstd = { version = "1.0.9", optional = true }
diff --git a/mesatee_services/acs/client/src/acs_client.rs b/mesatee_services/acs/client/src/acs_client.rs
new file mode 100644
index 0000000..9a1ceff
--- /dev/null
+++ b/mesatee_services/acs/client/src/acs_client.rs
@@ -0,0 +1,150 @@
+// Copyright 2019 MesaTEE Authors
+//
+// 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.
+
+// Insert std prelude in the top for the sgx feature
+#[cfg(feature = "mesalock_sgx")]
+use std::prelude::v1::*;
+
+use acs_proto::*;
+use mesatee_core::config::{OutboundDesc, TargetDesc};
+use mesatee_core::rpc::channel::SgxTrustedChannel;
+use mesatee_core::{Error, ErrorKind, Result};
+
+use std::collections::HashSet;
+
+pub struct ACSClient {
+ channel: SgxTrustedChannel<ACSRequest, ACSResponse>,
+}
+
+impl ACSClient {
+ pub fn new(target: TargetDesc) -> Result<Self> {
+ let addr = target.addr;
+ let channel = match target.desc {
+ OutboundDesc::Sgx(enclave_addr) => {
+ SgxTrustedChannel::<ACSRequest, ACSResponse>::new(addr, enclave_addr)?
+ }
+ };
+ Ok(ACSClient { channel })
+ }
+
+ pub fn enforce_task_launch(
+ &mut self,
+ task: String,
+ participants: HashSet<String>,
+ ) -> Result<bool> {
+ let req = ACSRequest::Enforce(EnforceRequest::LaunchTask(task, participants));
+ let resp = self.channel.invoke(req)?;
+ match resp {
+ ACSResponse::Enforce(allow) => Ok(allow),
+ _ => Err(Error::from(ErrorKind::RPCResponseError)),
+ }
+ }
+
+ pub fn enforce_data_access(
+ &mut self,
+ task: String,
+ data: String
+ ) -> Result<bool> {
+ let req = ACSRequest::Enforce(EnforceRequest::AccessData(task, data));
+ let resp = self.channel.invoke(req)?;
+ match resp {
+ ACSResponse::Enforce(allow) => Ok(allow),
+ _ => Err(Error::from(ErrorKind::RPCResponseError)),
+ }
+ }
+
+ pub fn enforce_data_deletion(
+ &mut self,
+ usr: String,
+ data: String,
+ ) -> Result<bool> {
+ let req = ACSRequest::Enforce(EnforceRequest::DeleteData(usr, data));
+ let resp = self.channel.invoke(req)?;
+ match resp {
+ ACSResponse::Enforce(allow) => Ok(allow),
+ _ => Err(Error::from(ErrorKind::RPCResponseError)),
+ }
+ }
+
+ pub fn enforce_script_access(
+ &mut self,
+ task: String,
+ script: String,
+ ) -> Result<bool> {
+ let req = ACSRequest::Enforce(EnforceRequest::AccessScript(task, script));
+ let resp = self.channel.invoke(req)?;
+ match resp {
+ ACSResponse::Enforce(allow) => Ok(allow),
+ _ => Err(Error::from(ErrorKind::RPCResponseError)),
+ }
+ }
+
+ pub fn enforce_script_deletion(
+ &mut self,
+ usr: String,
+ script: String
+ ) -> Result<bool> {
+ let req = ACSRequest::Enforce(EnforceRequest::DeleteScript(usr, script));
+ let resp = self.channel.invoke(req)?;
+ match resp {
+ ACSResponse::Enforce(allow) => Ok(allow),
+ _ => Err(Error::from(ErrorKind::RPCResponseError)),
+ }
+ }
+
+ fn _announce_terms(&mut self, facts: Vec<AccessControlTerms>) -> Result<()> {
+ let req = ACSRequest::Announce(AnnounceRequest { facts });
+ let resp = self.channel.invoke(req)?;
+ match resp {
+ ACSResponse::Announce => Ok(()),
+ _ => Err(Error::from(ErrorKind::RPCResponseError)),
+ }
+ }
+
+ pub fn announce_task_creation(
+ &mut self,
+ task: String,
+ creator: String,
+ participants: &HashSet<String>,
+ ) -> Result<()> {
+ let mut facts = Vec::with_capacity(1 + participants.len());
+ for par in participants {
+ facts.push(AccessControlTerms::TaskParticipant(task.clone(), par.clone()));
+ }
+ facts.push(AccessControlTerms::TaskCreator(task, creator));
+ self._announce_terms(facts)
+ }
+
+ pub fn announce_data_creation(
+ &mut self,
+ data: String,
+ creator: String,
+ ) -> Result<()> {
+ self._announce_terms(std::vec!(AccessControlTerms::DataOwner(data, creator)))
+ }
+
+ pub fn announce_script_creation(
+ &mut self,
+ script: String,
+ creator: String,
+ is_public: bool,
+ ) -> Result<()> {
+ let mut terms = Vec::new();
+ if is_public {
+ terms.push(AccessControlTerms::IsPublicScript(script.clone()))
+ }
+ terms.push(AccessControlTerms::ScriptOwner(script, creator));
+ self._announce_terms(terms)
+ }
+}
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs b/mesatee_services/acs/client/src/lib.rs
similarity index 74%
copy from tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
copy to mesatee_services/acs/client/src/lib.rs
index dde9bb0..f774564 100644
--- a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
+++ b/mesatee_services/acs/client/src/lib.rs
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-pub mod common_setup;
-pub mod kms_test;
-pub mod protected_fs_test;
-pub mod tdfs_test;
-pub mod tms_test;
+// Use sgx_tstd to replace Rust's default std
+#![cfg_attr(feature = "mesalock_sgx", no_std)]
+#[cfg(feature = "mesalock_sgx")]
+extern crate sgx_tstd as std;
+
+mod acs_client;
+pub use acs_client::ACSClient;
diff --git a/mesatee_services/acs/model.conf b/mesatee_services/acs/model.conf
new file mode 100644
index 0000000..7e30003
--- /dev/null
+++ b/mesatee_services/acs/model.conf
@@ -0,0 +1,32 @@
+[requests]
+launch_task = task, participants
+access_data = task, data
+delete_data = usr, data
+access_script = task, script
+delete_script = usr, script
+
+[terms]
+task_creator = task, usr
+task_participant = task, usr
+
+script_owner = script, usr
+is_public_script = script
+
+data_owner = data, usr
+
+[matchers]
+# All participants must approve task launch
+launch_task = task_participant(launch_task.task, _) <= launch_task.participants
+
+# All participants must approve data access
+access_data = data_owner(access_data.data, _) <= task_participant(access_data.task, _)
+
+# Any creator of the data can request for deletion
+delete_data = data_owner(delete_data.data, delete_data.usr)
+
+access_script = \
+ is_public_script(access_script.script) or \
+ script_owner(access_script.script, _) <= task_participant(access_script.task, _)
+
+# Only creator of the script can request for deletion
+delete_script = script_owner(delete_script.script, delete_script.usr)
\ No newline at end of file
diff --git a/mesatee_services/acs/proto/Cargo.toml b/mesatee_services/acs/proto/Cargo.toml
new file mode 100644
index 0000000..55be836
--- /dev/null
+++ b/mesatee_services/acs/proto/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "acs_proto"
+version = "0.1.0"
+authors = ["MesaTEE Authors <de...@mesatee.org>"]
+description = "RPC protocol of ACS."
+license = "Apache-2.0"
+edition = "2018"
+
+[features]
+default = []
+mesalock_sgx = ["sgx_tstd", "mesatee_core/mesalock_sgx"]
+cov = ["sgx_cov"]
+
+[dependencies]
+mesatee_core = { version = "0.1.0" }
+serde = { version = "1.0.39" }
+serde_json = { version = "1.0.39" }
+serde_derive = { version = "1.0.92" }
+uuid = { version = "0.7.4", features = ["v4"] }
+
+sgx_cov = { version = "0.1.0", optional = true }
+sgx_tstd = { version = "1.0.9", features = ["net", "backtrace"], optional = true }
+sgx_types = { version = "1.0.9" }
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs b/mesatee_services/acs/proto/src/lib.rs
similarity index 80%
copy from tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
copy to mesatee_services/acs/proto/src/lib.rs
index dde9bb0..ff8f727 100644
--- a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
+++ b/mesatee_services/acs/proto/src/lib.rs
@@ -11,9 +11,10 @@
// 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.
+#![cfg_attr(feature = "mesalock_sgx", no_std)]
+#[cfg(feature = "mesalock_sgx")]
-pub mod common_setup;
-pub mod kms_test;
-pub mod protected_fs_test;
-pub mod tdfs_test;
-pub mod tms_test;
+extern crate sgx_tstd as std;
+
+mod proto;
+pub use proto::*;
diff --git a/mesatee_services/acs/proto/src/proto.rs b/mesatee_services/acs/proto/src/proto.rs
new file mode 100644
index 0000000..78c63f1
--- /dev/null
+++ b/mesatee_services/acs/proto/src/proto.rs
@@ -0,0 +1,75 @@
+// Copyright 2019 MesaTEE Authors
+//
+// 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.
+
+// Insert std prelude in the top for the sgx feature
+#[cfg(feature = "mesalock_sgx")]
+use std::prelude::v1::*;
+
+use std::collections::HashSet;
+
+use serde_derive::*;
+
+#[derive(Clone, Serialize, Deserialize, Debug)]
+#[serde(tag = "type")]
+pub enum ACSRequest {
+ Enforce(EnforceRequest),
+ Announce(AnnounceRequest),
+}
+
+#[derive(Clone, Serialize, Deserialize, Debug)]
+pub enum ACSResponse {
+ Enforce(bool),
+ Announce,
+}
+
+#[derive(Clone, Serialize, Deserialize, Debug)]
+pub enum EnforceRequest {
+ // launch_task = task, participants
+ LaunchTask(String, HashSet<String>),
+
+ // access_data = task, data
+ AccessData(String, String),
+
+ // delete_data = usr, data
+ DeleteData(String, String),
+
+ // access_script = task, script
+ AccessScript(String, String),
+
+ // delete_script = usr, script
+ DeleteScript(String, String),
+}
+
+#[derive(Clone, Serialize, Deserialize, Debug)]
+pub struct AnnounceRequest {
+ pub facts: Vec<AccessControlTerms>,
+}
+
+#[derive(Clone, Serialize, Deserialize, Debug)]
+pub enum AccessControlTerms {
+ // task_creator = task, usr
+ TaskCreator(String, String),
+
+ // task_participant = task, usr
+ TaskParticipant(String, String),
+
+ // data_owner = data, usr
+ DataOwner(String, String),
+
+ // script_owner = script, usr
+ ScriptOwner(String, String),
+
+ // is_public_script = script
+ IsPublicScript(String),
+}
diff --git a/mesatee_services/acs/python/acs_engine.py b/mesatee_services/acs/python/acs_engine.py
new file mode 100644
index 0000000..4a12bf4
--- /dev/null
+++ b/mesatee_services/acs/python/acs_engine.py
@@ -0,0 +1,631 @@
+###############################################################################
+# Parser Combinators
+###############################################################################
+class Pair(tuple):
+ def __new__(cls, a, b):
+ return super(Pair, cls).__new__(cls, [a, b])
+
+class Either(object):
+ def __init__(self, left, right):
+ self.__left = left
+ self.__right = right
+
+ def left(self):
+ if not self.is_left():
+ raise ValueError('wrong extractor for either')
+ return self.__left
+
+ def right(self):
+ if not self.is_right():
+ raise ValueError('wrong extractor for either')
+ return self.__right
+
+ def is_right(self):
+ return False
+
+ def is_left(self):
+ return False
+
+ def get(self):
+ if self.is_right():
+ return self.right()
+ if self.is_left():
+ return self.left()
+ raise ValueError('incomplete Either object')
+
+ def __str__(self):
+ if self.is_left():
+ return 'Left(' + str(self.left()) + ')'
+ else:
+ return 'Right(' + str(self.right()) + ')'
+
+ def __repr__(self):
+ if self.is_left():
+ return 'Left(' + repr(self.left()) + ')'
+ else:
+ return 'Right(' + repr(self.right()) + ')'
+
+class Left(Either):
+ def __init__(self, payload):
+ super(Left, self).__init__(payload, None)
+
+ def is_left(self):
+ return True
+
+class Right(Either):
+ def __init__(self, payload):
+ super(Right, self).__init__(None, payload)
+
+ def is_right(self):
+ return True
+
+class Stream(object):
+ WHITESPACES = [' ', '\t', '\r']
+ def __init__(self, items, pos = 0):
+ self.__items = items
+ self.__pos = pos
+
+ def accept_strlit(self, string):
+ # Typically parsers want to skip white spaces except line breaks
+ # In the future this should be configurable
+ pos = self.__pos
+ l = len(self.__items)
+ while pos < l and self.__items[pos] in self.WHITESPACES:
+ pos += 1
+
+ match_pos = 0
+ l = len(string)
+ while match_pos < l and string[match_pos] in self.WHITESPACES:
+ match_pos += 1
+ if pos < match_pos:
+ raise ParsingError(self, 'expecting "{}"'.format(string))
+ if match_pos:
+ string = string[match_pos:]
+ if self.__items.startswith(string, pos):
+ return Stream(self.__items, pos + len(string))
+ raise ParsingError(self, 'expecting "{}"'.format(string))
+
+ def accept_matcher(self, matcher):
+ pos = self.__pos
+ l = len(self.__items)
+ while pos < l and self.__items[pos] in self.WHITESPACES:
+ pos += 1
+
+ res = matcher(self.__items, pos)
+ if res is None:
+ raise ParsingError(self, 'matcher for {} failed'.format(matcher.__doc__))
+ obj, npos = res
+ return obj, Stream(self.__items, npos)
+
+ def end(self):
+ return self.__pos == len(self.__items)
+
+ def pos(self):
+ return self.__pos
+
+ def __repr__(self):
+ line_start = self.__items.rfind('\n', 0, self.__pos) + 1
+ line_end = self.__items.find('\n', self.__pos)
+ if line_end == -1:
+ line_end = self.__pos
+
+ if line_end - line_start > 80:
+ line_start = max(line_start, self.__pos - 40)
+ line_end = min(line_start + 80, len(self.__items))
+
+ return ''.join([
+ self.__items[line_start:line_end],
+ '\n',
+ ' ' * (self.__pos - line_start),
+ '^',
+ ' ' * (line_end - self.__pos),
+ '\nerror at character ',
+ str(self.__pos),
+ ])
+
+class State(object):
+ def __init__(self, stream, payload = None, success = True):
+ self.stream = stream
+ self.payload = payload
+ self.success = success
+
+ def __bool__(self):
+ return self.success
+
+ def __nonzero__(self):
+ return self.__bool__()
+
+ def fmap(self, f):
+ if self:
+ return State(self.stream, f(self.payload))
+ return self
+
+class ParsingError(Exception):
+ def __init__(self, stream, msg = ''):
+ super(ParsingError, self).__init__(msg)
+ self.stream = stream
+
+ def __repr__(self):
+ return repr(self.stream)
+
+class Parser(object):
+ def __init__(self):
+ pass
+
+ def __call__(self, stream):
+ raise NotImplementedError("pure abstract parser cannot be called")
+
+ def parse_from(self, stream):
+ n_state = self(stream)
+ if not n_state:
+ raise ParsingError(n_state.stream, n_state.payload)
+ elif not n_state.stream.end():
+ raise ParsingError(n_state.stream, 'trailing unparsable input')
+ return n_state
+
+ def fail(self, exception):
+ return State(exception.stream, str(exception), False)
+
+ def ignore(self):
+ return Ignore(self)
+
+ def __or__(self, p):
+ return Or(self, p)
+
+ def __add__(self, p):
+ if isinstance(self, Ignore) and isinstance(p, Ignore):
+ return Ignore(Concat(self, p))
+ else:
+ return Concat(self, p)
+
+ def __invert__(self):
+ return Rep(self)
+
+ def __neg__(self):
+ return Optional(self)
+
+ def __pow__(self, f):
+ return Apply(self, f)
+
+class Optional(Parser):
+ def __init__(self, opt):
+ super(Optional, self).__init__()
+ self.__opt = opt
+
+ def __call__(self, stream):
+ n_state = self.__opt(stream)
+ if n_state:
+ return n_state.fmap(lambda x: Left(x))
+ return State(stream, Right(None))
+
+class StrLiteral(Parser):
+ def __init__(self, string):
+ super(StrLiteral, self).__init__()
+ self.__string = string
+
+ def __call__(self, stream):
+ if stream.end():
+ return self.fail(ParsingError(
+ stream, 'insufficient input, expecting {}'.format(self.__string))
+ )
+ try:
+ n_stream = stream.accept_strlit(self.__string)
+ except ParsingError as e:
+ return self.fail(e)
+
+ return State(n_stream, self.__string)
+
+class CustomMatcher(Parser):
+ def __init__(self, matcher):
+ super(CustomMatcher, self).__init__()
+ self.__matcher = matcher
+
+ def __call__(self, stream):
+ try:
+ res = stream.accept_matcher(self.__matcher)
+ except ParsingError as e:
+ return self.fail(e)
+
+ obj, n_stream = res
+ return State(n_stream, obj)
+
+class Concat(Parser):
+ def __init__(self, c1, c2):
+ super(Concat, self).__init__()
+ assert not isinstance(self, Ignore) or not isinstance(p, Ignore)
+ self.__first = c1
+ self.__second = c2
+
+ def __call__(self, stream):
+ n_state = self.__first(stream)
+ if not n_state:
+ return n_state
+ p1 = n_state.payload
+ n_state = self.__second(n_state.stream)
+ if not n_state:
+ return n_state
+ p2 = n_state.payload
+
+ if isinstance(self.__first, Ignore):
+ return State(n_state.stream, p2)
+ if isinstance(self.__second, Ignore):
+ return State(n_state.stream, p1)
+ # The construction of Concat ensures that at least
+ # one of this children is not Ignore
+ return State(n_state.stream, Pair(p1, p2))
+
+class Or(Parser):
+ def __init__(self, c1, c2):
+ super(Or, self).__init__()
+ self.__if = c1
+ self.__else = c2
+
+ def __call__(self, stream):
+ n_state = self.__if(stream)
+ if n_state:
+ return n_state.fmap(lambda x: Left(x))
+ n_state = self.__else(stream)
+ if n_state:
+ return n_state.fmap(lambda x: Right(x))
+ return n_state
+
+class Rep(Parser):
+ def __init__(self, c):
+ super(Rep, self).__init__()
+ self.__loop = c
+
+ def __call__(self, stream):
+ payload = []
+
+ n_state = self.__loop(stream)
+ if n_state:
+ payload.append(n_state.payload)
+ stream = n_state.stream
+ n_state = self(stream)
+ if n_state:
+ payload = payload + n_state.payload
+ stream = n_state.stream
+ return State(stream, payload)
+
+class Apply(Parser):
+ def __init__(self, base, f):
+ super(Apply, self).__init__()
+ self.__base = base
+ self.__trans = f
+
+ def __call__(self, stream):
+ return self.__base(stream).fmap(self.__trans)
+
+class Ignore(Parser):
+ def __init__(self, base):
+ super(Ignore, self).__init__()
+ self.__base = base
+
+ def __call__(self, stream):
+ return self.__base(stream)
+
+###############################################################################
+# Grammars for PERM model configuration
+###############################################################################
+from operator import or_, add
+
+def extract(nested_or):
+ while isinstance(nested_or, Either):
+ nested_or = nested_or.left() if nested_or.is_left() else nested_or.right()
+ return nested_or
+
+def flatten(nested_concat):
+ res = []
+
+ def pre_order(pair, res):
+ if isinstance(pair, Pair):
+ pre_order(pair[0], res)
+ pre_order(pair[1], res)
+ else:
+ res.append(pair)
+
+ pre_order(nested_concat, res)
+ return res
+
+def one_of(parsers):
+ nested = reduce(or_, parsers)
+ return nested ** extract
+
+def join(sl):
+ return ''.join(sl)
+
+def rep_with_sep(to_rep, sep):
+ if not isinstance(sep, Ignore):
+ sep = sep.ignore()
+ r = to_rep + ~(sep + to_rep)
+ r = r ** (lambda x: [x[0]] + x[1])
+ return r
+
+ALPHA = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+DIGIT = set('0123456789')
+ALPHA_DIGIT = ALPHA | DIGIT
+
+Alpha = one_of(map(StrLiteral, ALPHA))
+Digit = one_of(map(StrLiteral, DIGIT))
+
+Equal, Comma, Dot = [StrLiteral(c).ignore() for c in ['=', ',', '.']]
+Underscore = StrLiteral('_')
+NewLine = (~ StrLiteral('\n')).ignore()
+
+def identifier_matcher(text, pos):
+ """identifier"""
+ end = len(text)
+ start = pos
+ if pos >= end:
+ return None
+ first = text[pos]
+ if first != '_' and first not in ALPHA:
+ return None
+ pos += 1
+ while pos < end:
+ char = text[pos]
+ if char == '_' or char in ALPHA_DIGIT:
+ pos += 1
+ else:
+ break
+ return text[start:pos], pos
+
+Identifier = CustomMatcher(identifier_matcher)
+
+IdTuple = rep_with_sep(Identifier, Comma)
+
+Definition = Identifier + Equal + IdTuple + NewLine
+
+Relation = Identifier + Equal + IdTuple + NewLine
+Relation = Relation ** (lambda x: (x[0], 1 + len(x[1][1])))
+
+def pyparser_matcher(text, pos):
+ """syntactically correct python code"""
+ line_end = text.find('\n', pos)
+ if line_end == -1:
+ return None
+ try:
+ c = compile(text[pos:line_end], '__abac_model__.py', 'eval')
+ except SyntaxError:
+ return None
+ return c, line_end
+
+PyExpr = CustomMatcher(pyparser_matcher)
+Matcher = Identifier + Equal + PyExpr + NewLine
+
+RequestDefHeader = StrLiteral('[requests]') + NewLine
+TermDefHeader = StrLiteral('[terms]') + NewLine
+MatchersHeader = StrLiteral('[matchers]') + NewLine
+
+RequestDefSec = RequestDefHeader.ignore() + ~Definition
+TermDefSec = TermDefHeader.ignore() + ~Definition
+MatchersSec = MatchersHeader.ignore() + ~Matcher
+
+ModelDef = (RequestDefSec + TermDefSec + MatchersSec) ** flatten
+
+def preprocess(conf):
+ # process escaped line breaks
+ conf = conf.replace('\\\n', '')
+ # remove comments
+ conf = '\n'.join(line.partition('#')[0] for line in conf.splitlines())
+ # remove redundant new lines
+ conf = conf.strip()
+
+ return conf + '\n'
+
+def parse_model(text):
+ text = preprocess(text)
+ raw_model = ModelDef.parse_from(Stream(text)).payload
+ return raw_model
+
+class InvalidModelDefinition(Exception):
+ def __init__(self, msg = ''):
+ super(InvalidModelDefinition, self).__init__(msg)
+
+ @staticmethod
+ def redundant_def(redefined_vars, g1, g2):
+ msg_parts = [
+ 'multiple definition(s) of identifiers(s)',
+ ', '.join(redfined_vars),
+ 'found in sections',
+ g1, g2
+ ]
+ return InvalidModelDefinition(''.join(msg_parts))
+
+ @staticmethod
+ def missing_matchers(missing_matchers):
+ msg = 'missing matcher(s) for request type(s): {}'
+ return InvalidModelDefinition(msg.format(', '.join(missing_matchers)))
+
+ @staticmethod
+ def unknown_requests(unknown_requests):
+ msg = 'matcher(s) defined for unknown request type(s): {}'
+ return InvalidModelDefinition(msg.format(', '.join(unknown_requests)))
+
+class Request(object):
+ def __init__(self, attrs, vals):
+ assert len(attrs) == len(vals)
+ self.__named_attrs = attrs
+ for attr, val in zip(attrs, vals):
+ setattr(self, attr, val)
+
+ def __repr__(self):
+ parts = ['Request {\n']
+ for attr in self.__named_attrs:
+ parts.append(' ')
+ parts.append(attr)
+ parts.append(': ')
+ parts.append(repr(getattr(self, attr)))
+ parts.append('\n')
+ parts.append('}\n')
+ return ''.join(parts)
+
+class QueryResult(object):
+ def __init__(self, generator):
+ self.__gen = generator
+
+ def __iter__(self):
+ return self.__gen
+
+ def __le__(self, iterable):
+ return set(self) <= set(iterable)
+
+ def __lt__(self, iterable):
+ return set(self) < set(iterable)
+
+ def __ge__(self, iterable):
+ return set(self) >= set(iterable)
+
+ def __gt__(self, iterable):
+ return set(self) > set(iterable)
+
+class Term(object):
+ PLACEHOLDER = object()
+ WILDCARD = None
+ def __init__(self, arity):
+ self.__arity = arity
+ self.__facts = set()
+
+ def add_facts(self, facts):
+ for fact in facts:
+ self.add_fact(fact)
+
+ def add_fact(self, fact):
+ assert len(fact) == self.__arity
+ if not isinstance(fact, tuple):
+ fact = tuple(fact)
+ self.__facts.add(fact)
+
+ def __call__(self, *args):
+ assert len(args) == self.__arity
+ # When all arguments are concrete, calling a term just returns boolean results
+ # indicating whether the called tuple is part of the known facts
+ n_placeholders = sum(arg is Term.PLACEHOLDER for arg in args)
+ if not n_placeholders:
+ return any(all(a == b for a, b in zip(fact, args)) for fact in self.__facts)
+ # If arguments contain one or more placeholders, calling a term is more like a
+ # query. The call returns a generator that iterates all facts that match with
+ # the pattern described by the arguments
+ def gen():
+ for fact in self.__facts:
+ rns = []
+ matched = True
+ for a, b in zip(fact, args):
+ if b is Term.PLACEHOLDER:
+ rns.append(a)
+ else:
+ if a != b:
+ matched = False
+ break
+ if matched:
+ if n_placeholders == 1:
+ yield rns[0]
+ else:
+ yield tuple(rns)
+ return QueryResult(gen())
+
+class Model(object):
+ def __init__(self, raw_model):
+ request_def, term_def, matchers = raw_model
+ self.__request_template = { r[0]:r[1] for r in request_def }
+ self.__term_template = { t[0]:t[1] for t in term_def }
+ self.__matchers = { m[0]:m[1] for m in matchers }
+
+ def_sections = zip(
+ ['[requests]', '[terms]'],
+ [self.__request_template, self.__term_template],
+ )
+
+ n_sec = len(def_sections)
+ for i in range(n_sec):
+ for j in range(i + 1, n_sec):
+ overlap = set(def_sections[i][1].keys()) & set(def_sections[j][1].keys())
+ if overlap:
+ raise InvalidModelDefinition.redundant_def(
+ overalp, def_sections[i][0], def_sections[j][0]
+ )
+
+ missing_matchers = set(self.__request_template.keys()) - set(self.__matchers.keys())
+ if missing_matchers:
+ raise InvalidModelDefinition.missing_matchers(missing_matchers)
+
+ unknown_requests = set(self.__matchers.keys()) - set(self.__request_template.keys())
+ if unknown_requests:
+ raise InvalidModelDefinition.unknown_requests(unknown_requests)
+
+ self.__term_knowledge_base = {
+ term_name:Term(len(term_tpl)) for term_name, term_tpl in self.__term_template.items()
+ }
+
+ def add_term_items(self, term_items):
+ for ti in term_items:
+ self.add_term_item(ti[0], ti[1:])
+
+ def add_term_item(self, term_name, fact):
+ term = self.__term_knowledge_base[term_name]
+ term.add_fact(fact)
+
+ def get_matcher_proxy(self, request_type, env):
+ def matcher_proxy():
+ return eval(self.__matchers[request_type], env)
+ return matcher_proxy
+
+ def enforce(self, request_type, request_content):
+ tpl = self.__request_template[request_type]
+ request = Request(tpl, request_content)
+
+ enforcer_env = {
+ request_type: request,
+ 'true': True, 'false': False, 'null': None,
+ '_': Term.PLACEHOLDER,
+ 'X': Term.WILDCARD,
+ }
+ enforcer_env.update(self.__term_knowledge_base)
+
+ return eval(self.__matchers[request_type], enforcer_env)
+
+global_perm_model = None
+
+if __name__ == '__builtin__':
+ from acs_py_enclave import ffi
+else:
+ class ffi:
+ @staticmethod
+ def def_extern():
+ return lambda x: x
+
+ @staticmethod
+ def string(s):
+ return s
+
+@ffi.def_extern()
+def acs_setup_model(conf):
+ try:
+ global global_perm_model
+ conf = ffi.string(conf)
+ global_perm_model = Model(parse_model(conf))
+ except:
+ return -1
+ return 0
+
+@ffi.def_extern()
+def acs_enforce_request(request_type, request_content):
+ try:
+ request_type = ffi.string(request_type)
+ # request_content is a list of ffi c strings which are syntactically valid
+ # python primitive-type objects, including strings, integers, foating point
+ # numbers, and lists/dictionaries of primitive-type objects
+ request_content = eval(ffi.string(request_content))
+ return global_perm_model.enforce(request_type, request_content)
+ except:
+ return -1
+
+@ffi.def_extern()
+def acs_announce_fact(term_type, term_fact):
+ try:
+ term_type = ffi.string(term_type)
+ term_fact = eval(ffi.string(term_fact))
+ global_perm_model.add_term_item(term_type, term_fact)
+ except:
+ return -1
+ return 0
diff --git a/mesatee_services/acs/python/acs_engine_test.py b/mesatee_services/acs/python/acs_engine_test.py
new file mode 100644
index 0000000..b951d41
--- /dev/null
+++ b/mesatee_services/acs/python/acs_engine_test.py
@@ -0,0 +1,82 @@
+if __name__ == '__main__':
+ import sys
+ import os
+ from acs_engine import *
+
+ model_path = os.path.join(os.path.dirname(__file__), '../model.conf')
+ test_model = open(model_path).read()
+ acs_setup_model(test_model)
+
+ FUSION_TASK = "data_fusion"
+ FUSION_TASK_PARTY_1 = "usr_party1"
+ FUSION_TASK_DATA_1 = "data1"
+ FUSION_TASK_PARTY_2 = "usr_party2"
+ FUSION_TASK_DATA_2 = "data2"
+ FUSION_TASK_SCRIPT = "fusion_script"
+ FUSION_TASK_SCRIPT_WRITER = "usr_party3"
+ PUBLIC_SCRIPT = "public_script"
+ PUBLIC_SCRIPT_WRITER = "usr_party4"
+
+ IRRELEVANT_TASK = "task_irrelevant"
+ IRRELEVANT_PARTY = "usr_irrelevant"
+ IRRELEVANT_DATA = "data_irrelevant"
+
+ acs_announce_fact('task_creator', repr([FUSION_TASK, FUSION_TASK_PARTY_1]))
+ acs_announce_fact('task_participant', repr([FUSION_TASK, FUSION_TASK_PARTY_1]))
+ acs_announce_fact('task_participant', repr([FUSION_TASK, FUSION_TASK_PARTY_2]))
+
+ acs_announce_fact('data_owner', repr([FUSION_TASK_DATA_1, FUSION_TASK_PARTY_1]))
+ acs_announce_fact('data_owner', repr([FUSION_TASK_DATA_2, FUSION_TASK_PARTY_2]))
+ acs_announce_fact('data_owner', repr([IRRELEVANT_DATA, IRRELEVANT_PARTY]))
+
+ acs_announce_fact('script_owner', repr([FUSION_TASK_SCRIPT, FUSION_TASK_SCRIPT_WRITER]))
+
+ acs_announce_fact('script_owner', repr([PUBLIC_SCRIPT, PUBLIC_SCRIPT_WRITER]))
+ acs_announce_fact('is_public_script', repr([PUBLIC_SCRIPT]))
+
+
+ assert acs_enforce_request('launch_task', repr([FUSION_TASK, set([FUSION_TASK_PARTY_1, FUSION_TASK_PARTY_2])]))
+ assert not acs_enforce_request('launch_task', repr([FUSION_TASK, set()]))
+ assert not acs_enforce_request('launch_task', repr([FUSION_TASK, set([FUSION_TASK_PARTY_1])]))
+ assert not acs_enforce_request('launch_task', repr([FUSION_TASK, set([FUSION_TASK_PARTY_2])]))
+
+ assert acs_enforce_request('access_data', repr([FUSION_TASK, FUSION_TASK_DATA_1]))
+ assert acs_enforce_request('access_data', repr([FUSION_TASK, FUSION_TASK_DATA_2]))
+ assert not acs_enforce_request('access_data', repr([FUSION_TASK, IRRELEVANT_DATA]))
+
+ assert acs_enforce_request('access_script', repr([FUSION_TASK, PUBLIC_SCRIPT]))
+ assert not acs_enforce_request('access_script', repr([FUSION_TASK, FUSION_TASK_SCRIPT]))
+
+ acs_announce_fact('task_participant', repr([FUSION_TASK, FUSION_TASK_SCRIPT_WRITER]))
+ assert acs_enforce_request('access_script', repr([FUSION_TASK, FUSION_TASK_SCRIPT]))
+
+ acs_announce_fact('task_participant', repr([FUSION_TASK, IRRELEVANT_PARTY]))
+ assert acs_enforce_request('access_script', repr([FUSION_TASK, FUSION_TASK_SCRIPT]))
+
+ acs_announce_fact('task_creator', repr([IRRELEVANT_TASK, IRRELEVANT_PARTY]))
+ acs_announce_fact('task_participant', repr([IRRELEVANT_TASK, IRRELEVANT_PARTY]))
+ acs_announce_fact('task_participant', repr([IRRELEVANT_TASK, FUSION_TASK_PARTY_2]))
+
+ assert acs_enforce_request('launch_task', repr([IRRELEVANT_TASK, set([IRRELEVANT_PARTY, FUSION_TASK_PARTY_2])]))
+ assert not acs_enforce_request('access_data', repr([IRRELEVANT_TASK, FUSION_TASK_DATA_1]))
+ assert acs_enforce_request('access_data', repr([IRRELEVANT_TASK, FUSION_TASK_DATA_2]))
+ assert not acs_enforce_request('access_script', repr([IRRELEVANT_TASK, FUSION_TASK_SCRIPT]))
+ assert acs_enforce_request('access_script', repr([IRRELEVANT_TASK, PUBLIC_SCRIPT]))
+
+ assert acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_1, FUSION_TASK_DATA_1]))
+ assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_1, FUSION_TASK_DATA_2]))
+ assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_1, IRRELEVANT_DATA]))
+ assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_1, FUSION_TASK_SCRIPT]))
+ assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_1, PUBLIC_SCRIPT]))
+
+ assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_2, FUSION_TASK_DATA_1]))
+ assert acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_2, FUSION_TASK_DATA_2]))
+ assert not acs_enforce_request('delete_data', repr([FUSION_TASK_PARTY_2, IRRELEVANT_DATA]))
+ assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_2, FUSION_TASK_SCRIPT]))
+ assert not acs_enforce_request('delete_script', repr([FUSION_TASK_PARTY_2, PUBLIC_SCRIPT]))
+
+ assert not acs_enforce_request('delete_data', repr([FUSION_TASK_SCRIPT_WRITER, FUSION_TASK_DATA_1]))
+ assert not acs_enforce_request('delete_data', repr([FUSION_TASK_SCRIPT_WRITER, FUSION_TASK_DATA_2]))
+ assert not acs_enforce_request('delete_data', repr([FUSION_TASK_SCRIPT_WRITER, IRRELEVANT_DATA]))
+ assert acs_enforce_request('delete_script', repr([FUSION_TASK_SCRIPT_WRITER, FUSION_TASK_SCRIPT]))
+ assert not acs_enforce_request('delete_script', repr([FUSION_TASK_SCRIPT_WRITER, PUBLIC_SCRIPT]))
diff --git a/mesatee_services/acs/python/ffi.py b/mesatee_services/acs/python/ffi.py
new file mode 100644
index 0000000..3f56be7
--- /dev/null
+++ b/mesatee_services/acs/python/ffi.py
@@ -0,0 +1,15 @@
+import os
+import sgx_cffi
+import _cffi_backend as backend
+
+ffi = sgx_cffi.FFI(backend)
+
+ffi.embedding_api("int acs_setup_model(const char *configuration);")
+ffi.embedding_api("""int acs_enforce_request(const char *request_type,
+ const char *request_content);""")
+ffi.embedding_api("""int acs_announce_fact(const char *term_type,
+ const char *term_fact);""")
+with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "acs_engine.py")) as f:
+ ffi.embedding_init_code(f.read())
+ffi.set_source('acs_py_enclave', '')
+ffi.emit_c_code(os.environ.get('PYPY_FFI_OUTDIR', ".") + "/acs_py_enclave.c")
diff --git a/mesatee_services/acs/sgx_app/Cargo.toml b/mesatee_services/acs/sgx_app/Cargo.toml
new file mode 100644
index 0000000..6119b11
--- /dev/null
+++ b/mesatee_services/acs/sgx_app/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "acs"
+version = "0.1.0"
+authors = ["MesaTEE Authors <de...@mesatee.org>"]
+description = "Access control service."
+license = "Apache-2.0"
+build = "build.rs"
+edition = "2018"
+
+[features]
+default = []
+
+[dependencies]
+sgx_types = { version = "1.0.9" }
+sgx_urts = { version = "1.0.9" }
+
+mesatee_core = { version = "0.1.0" }
+mesatee_binder = { version = "0.1.0" }
+
+ctrlc = { version = "3.1.2" }
+log = { version = "0.4.6" }
+env_logger = { version = "0.6.1" }
+threadpool = { version = "1.0" }
diff --git a/mesatee_services/acs/sgx_app/build.rs b/mesatee_services/acs/sgx_app/build.rs
new file mode 100644
index 0000000..df76028
--- /dev/null
+++ b/mesatee_services/acs/sgx_app/build.rs
@@ -0,0 +1,61 @@
+// Copyright 2019 MesaTEE Authors
+//
+// 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.
+
+use std::env;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn choose_sgx_dylib(is_sim: bool) {
+ if is_sim {
+ println!("cargo:rustc-link-lib=dylib=sgx_urts_sim");
+ println!("cargo:rustc-link-lib=dylib=sgx_uae_service_sim");
+ } else {
+ println!("cargo:rustc-link-lib=dylib=sgx_urts");
+ println!("cargo:rustc-link-lib=dylib=sgx_uae_service");
+ }
+}
+
+fn main() {
+ let sdk_dir = env::var("SGX_SDK").unwrap_or("/opt/intel/sgxsdk".into());
+ println!("cargo:rustc-link-search=native={}/lib64", sdk_dir);
+
+ // This would triggers `unwrap()` which results in panic, if no such env
+ // var found. Cargo documents say that this env variable is provided by
+ // cargo. See
+ // https://doc.rust-lang.org/cargo/reference/environment-variables.html
+ let enclave_name = env!("CARGO_PKG_NAME");
+
+ // Once we enclave_name ready, write it to `../pkg_name`
+ std::fs::File::create("../pkg_name")
+ .unwrap()
+ .write_all(enclave_name.as_bytes())
+ .unwrap();
+
+ let out_path = env::var_os("ENCLAVE_OUT_DIR").unwrap_or("out".into());
+ let out_dir = &PathBuf::from(out_path);
+
+ println!("cargo:rustc-link-search=native={}", out_dir.display());
+ println!("cargo:rustc-link-lib=static=Enclave_u");
+
+ let is_sim = match env::var("SGX_MODE") {
+ Ok(ref v) if v == "SW" => true,
+ Ok(ref v) if v == "HW" => false,
+ Err(env::VarError::NotPresent) => false,
+ _ => {
+ panic!("Stop build process, wrong SGX_MODE env provided.");
+ }
+ };
+
+ choose_sgx_dylib(is_sim);
+}
diff --git a/mesatee_services/acs/sgx_app/src/main.rs b/mesatee_services/acs/sgx_app/src/main.rs
new file mode 100644
index 0000000..63ed44f
--- /dev/null
+++ b/mesatee_services/acs/sgx_app/src/main.rs
@@ -0,0 +1,86 @@
+// Copyright 2019 MesaTEE Authors
+//
+// 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.
+
+#[macro_use]
+extern crate log;
+
+use mesatee_core::prelude::*;
+use mesatee_core::{config, Result};
+
+use std::net::TcpListener;
+use std::os::unix::io::IntoRawFd;
+use threadpool::ThreadPool;
+
+use mesatee_binder::TeeBinder;
+use std::sync::Arc;
+
+fn main() -> Result<()> {
+ env_logger::init();
+
+ let tee = match TeeBinder::new("acs", 1) {
+ Ok(r) => {
+ info!("Init TEE Successfully!");
+ r
+ }
+ Err(x) => {
+ error!("Init TEE Failed {}!", x);
+ std::process::exit(-1)
+ }
+ };
+
+ let tee = Arc::new(tee);
+
+ {
+ let ref_tee = tee.clone();
+ ctrlc::set_handler(move || {
+ info!("\nCTRL+C pressed. Destroying server enclave");
+ let _ = ref_tee.finalize();
+ std::process::exit(0);
+ })
+ .expect("Error setting Ctrl-C handler");
+ }
+
+ run_access_control_service(tee)?;
+
+ Ok(())
+}
+
+fn run_access_control_service(tee: Arc<TeeBinder>) -> Result<()> {
+ info!("Running as ACS Server ...");
+
+ let config = config::Internal::acs();
+ let listener = TcpListener::bind(config.addr)?;
+ let port = config.addr.port();
+
+ let n_workers = 10;
+ let pool = ThreadPool::new(n_workers);
+ for stream in listener.incoming() {
+ match stream {
+ Ok(stream) => {
+ let tee = tee.clone();
+ pool.execute(move || {
+ debug!("new worker from {:?}", stream.peer_addr());
+ let fd = stream.into_raw_fd();
+ let input = ServeConnectionInput::new(fd, port);
+ let cmd = ECallCommand::ServeConnection;
+ let _ = tee
+ .invoke::<ServeConnectionInput, ServeConnectionOutput>(cmd.into(), input);
+ });
+ }
+ Err(e) => warn!("couldn't get client: {:?}", e),
+ }
+ }
+
+ Ok(())
+}
diff --git a/mesatee_services/acs/sgx_trusted_lib/Cargo.toml b/mesatee_services/acs/sgx_trusted_lib/Cargo.toml
new file mode 100644
index 0000000..94973f3
--- /dev/null
+++ b/mesatee_services/acs/sgx_trusted_lib/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "acs_enclave"
+version = "0.1.0"
+authors = ["MesaTEE Authors <de...@mesatee.org>"]
+description = "Access control service."
+license = "Apache-2.0"
+edition = "2018"
+
+[lib]
+name = "acs_enclave"
+crate-type = ["staticlib"]
+
+[features]
+default = []
+mesalock_sgx = ["sgx_tstd", "mesatee_core/mesalock_sgx", "acs_proto/mesalock_sgx"]
+cov = ["sgx_cov"]
+
+[dependencies]
+cfg-if = { version = "0.1.9" }
+lazy_static = { version = "1.0.2", features = ["spin_no_std"] }
+log = { version = "0.4.6" }
+env_logger = { version = "0.6.1" }
+uuid = { version = "0.7.4", features = ["v4"] }
+
+acs_proto = { path = "../proto", optional = true}
+mesatee_core = { version = "0.1.0" }
+
+sgx_cov = { version = "0.1.0", optional = true }
+sgx_tstd = { version = "1.0.9", features = ["net", "backtrace"], optional = true }
+sgx_types = { version = "1.0.9" }
diff --git a/mesatee_services/acs/sgx_trusted_lib/Enclave.config.xml b/mesatee_services/acs/sgx_trusted_lib/Enclave.config.xml
new file mode 100644
index 0000000..705edcd
--- /dev/null
+++ b/mesatee_services/acs/sgx_trusted_lib/Enclave.config.xml
@@ -0,0 +1,12 @@
+<!-- Please refer to User's Guide for the explanation of each field -->
+<EnclaveConfiguration>
+ <ProdID>0</ProdID>
+ <ISVSVN>0</ISVSVN>
+ <StackMaxSize>0x200000</StackMaxSize>
+ <HeapMaxSize>0x1000000</HeapMaxSize>
+ <TCSNum>22</TCSNum>
+ <TCSPolicy>0</TCSPolicy>
+ <DisableDebug>0</DisableDebug>
+ <MiscSelect>0</MiscSelect>
+ <MiscMask>0xFFFFFFFF</MiscMask>
+</EnclaveConfiguration>
diff --git a/mesatee_services/acs/sgx_trusted_lib/src/acs.rs b/mesatee_services/acs/sgx_trusted_lib/src/acs.rs
new file mode 100644
index 0000000..4e2c735
--- /dev/null
+++ b/mesatee_services/acs/sgx_trusted_lib/src/acs.rs
@@ -0,0 +1,231 @@
+// Copyright 2019 MesaTEE Authors
+//
+// 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.
+
+// Insert std prelude in the top for the sgx feature
+#[cfg(feature = "mesalock_sgx")]
+use std::prelude::v1::*;
+
+use mesatee_core::rpc::EnclaveService;
+use mesatee_core::{Result, ErrorKind};
+use std::ffi::CString;
+use std::os::raw::c_char;
+use std::collections::HashSet;
+
+use acs_proto::*;
+
+pub trait PyMarshallable {
+ fn marshal(&self, buffer: &mut String);
+}
+
+impl<T> PyMarshallable for (T,)
+where
+ T: PyMarshallable,
+{
+ fn marshal(&self, buffer: &mut String) {
+ buffer.push('[');
+ self.0.marshal(buffer);
+ buffer.push(']');
+ }
+}
+
+impl<U, V> PyMarshallable for (U, V)
+where
+ U: PyMarshallable,
+ V: PyMarshallable,
+{
+ fn marshal(&self, buffer: &mut String) {
+ buffer.push('[');
+ self.0.marshal(buffer);
+ buffer.push(',');
+ self.1.marshal(buffer);
+ buffer.push(']');
+ }
+}
+
+impl<X, Y, Z> PyMarshallable for (X, Y, Z)
+where
+ X: PyMarshallable,
+ Y: PyMarshallable,
+ Z: PyMarshallable,
+{
+ fn marshal(&self, buffer: &mut String) {
+ buffer.push('[');
+ self.0.marshal(buffer);
+ buffer.push(',');
+ self.1.marshal(buffer);
+ buffer.push(',');
+ self.2.marshal(buffer);
+ buffer.push(']');
+ }
+}
+
+impl<T> PyMarshallable for [T] where T: PyMarshallable {
+ fn marshal(&self, buffer: &mut String) {
+ buffer.push('[');
+ for t in self.as_ref() {
+ t.marshal(buffer);
+ buffer.push(',');
+ }
+ buffer.push(']');
+ }
+}
+
+impl<T: PyMarshallable> PyMarshallable for &HashSet<T> {
+ fn marshal(&self, buffer: &mut String) {
+ buffer.push_str("set([");
+ for t in *self {
+ t.marshal(buffer);
+ buffer.push(',');
+ }
+ buffer.push_str("])");
+ }
+}
+
+impl PyMarshallable for String {
+ fn marshal(&self, buffer: &mut String) {
+ buffer.push('\'');
+ buffer.push_str(self);
+ buffer.push('\'');
+ }
+}
+
+impl PyMarshallable for &String {
+ fn marshal(&self, buffer: &mut String) {
+ buffer.push('\'');
+ buffer.push_str(self);
+ buffer.push('\'');
+ }
+}
+
+pub trait HandleRequest {
+ fn handle_request(&self) -> Result<ACSResponse>;
+}
+
+extern "C" {
+ fn acs_enforce_request(request_type: *const c_char, request_content: *const c_char) -> i32;
+ fn acs_announce_fact(fact_type: *const c_char, fact_vals: *const c_char) -> i32;
+}
+
+impl HandleRequest for EnforceRequest {
+ fn handle_request(&self) -> Result<ACSResponse> {
+ let (request_type, request_content) = match self {
+ EnforceRequest::LaunchTask(usr, task) => {
+ let mut buffer = String::new();
+ (usr, task).marshal(&mut buffer);
+ ("launch_task", buffer)
+ }
+ EnforceRequest::AccessData(task, data) => {
+ let mut buffer = String::new();
+ (task, data).marshal(&mut buffer);
+ ("access_data", buffer)
+ }
+ EnforceRequest::DeleteData(usr, data) => {
+ let mut buffer = String::new();
+ (usr, data).marshal(&mut buffer);
+ ("delete_data", buffer)
+ }
+ EnforceRequest::AccessScript(task, script) => {
+ let mut buffer = String::new();
+ (task, script).marshal(&mut buffer);
+ ("access_script", buffer)
+ }
+ EnforceRequest::DeleteScript(usr, script) => {
+ let mut buffer = String::new();
+ (usr, script).marshal(&mut buffer);
+ ("delete_script", buffer)
+ }
+ };
+
+ let c_request_type = CString::new(request_type.to_string()).unwrap();
+ let c_request_content = CString::new(request_content).unwrap();
+ let py_ret = unsafe {
+ acs_enforce_request(c_request_type.as_ptr(), c_request_content.as_ptr())
+ };
+
+ match py_ret {
+ 0 => Ok(ACSResponse::Enforce(false)),
+ 1 => Ok(ACSResponse::Enforce(true)),
+ _ => Err(ErrorKind::MesaPyError.into()),
+ }
+ }
+}
+
+impl HandleRequest for AnnounceRequest {
+ fn handle_request(&self) -> Result<ACSResponse> {
+ for fact in &self.facts {
+ use AccessControlTerms::*;
+ let (term_type, term_fact) = match fact {
+ TaskCreator(task, usr) => {
+ let mut buffer = String::new();
+ (task, usr).marshal(&mut buffer);
+ ("task_creator", buffer)
+ }
+ TaskParticipant(task, usr) => {
+ let mut buffer = String::new();
+ (task, usr).marshal(&mut buffer);
+ ("task_participant", buffer)
+ }
+ DataOwner(data, usr) => {
+ let mut buffer = String::new();
+ (data, usr).marshal(&mut buffer);
+ ("data_owner", buffer)
+ }
+ ScriptOwner(script, usr) => {
+ let mut buffer = String::new();
+ (script, usr).marshal(&mut buffer);
+ ("script_owner", buffer)
+ }
+ IsPublicScript(script) => {
+ let mut buffer = String::new();
+ (script,).marshal(&mut buffer);
+ ("is_public_script", buffer)
+ }
+ };
+
+ let c_term_type = CString::new(term_type.to_string()).unwrap();
+ let c_term_fact = CString::new(term_fact).unwrap();
+
+ let py_ret = unsafe {
+ acs_announce_fact(c_term_type.as_ptr(), c_term_fact.as_ptr())
+ };
+
+ if py_ret != 0 {
+ return Err(ErrorKind::MesaPyError.into());
+ }
+ }
+ Ok(ACSResponse::Announce)
+ }
+}
+
+pub struct ACSEnclave;
+
+impl Default for ACSEnclave {
+ fn default() -> Self {
+ ACSEnclave {}
+ }
+}
+
+impl EnclaveService<ACSRequest, ACSResponse> for ACSEnclave {
+ fn handle_invoke(&mut self, input: ACSRequest) -> Result<ACSResponse> {
+ debug!("handle_invoke invoked!");
+ debug!("incoming payload = {:?}", input);
+
+ let response = match input {
+ ACSRequest::Enforce(req) => req.handle_request()?,
+ ACSRequest::Announce(req) => req.handle_request()?,
+ };
+
+ Ok(response)
+ }
+}
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs b/mesatee_services/acs/sgx_trusted_lib/src/lib.rs
similarity index 68%
copy from tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
copy to mesatee_services/acs/sgx_trusted_lib/src/lib.rs
index dde9bb0..d83f8f7 100644
--- a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
+++ b/mesatee_services/acs/sgx_trusted_lib/src/lib.rs
@@ -11,9 +11,20 @@
// 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.
+#![cfg_attr(feature = "mesalock_sgx", no_std)]
+#[cfg(feature = "mesalock_sgx")]
+#[macro_use]
+extern crate sgx_tstd as std;
-pub mod common_setup;
-pub mod kms_test;
-pub mod protected_fs_test;
-pub mod tdfs_test;
-pub mod tms_test;
+#[macro_use]
+extern crate log;
+
+mod acs;
+
+use cfg_if::cfg_if;
+cfg_if! {
+ if #[cfg(feature = "mesalock_sgx")] {
+ mod sgx;
+ } else {
+ }
+}
diff --git a/mesatee_services/acs/sgx_trusted_lib/src/sgx.rs b/mesatee_services/acs/sgx_trusted_lib/src/sgx.rs
new file mode 100644
index 0000000..85266e6
--- /dev/null
+++ b/mesatee_services/acs/sgx_trusted_lib/src/sgx.rs
@@ -0,0 +1,106 @@
+// Copyright 2019 MesaTEE Authors
+//
+// 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.
+
+// Insert std prelude in the top for the sgx feature
+#[cfg(feature = "mesalock_sgx")]
+use std::prelude::v1::*;
+
+use std::ffi::CString;
+use std::os::raw::c_char;
+
+use mesatee_core::config;
+use mesatee_core::prelude::*;
+use mesatee_core::{Result, Error, ErrorKind};
+
+use env_logger;
+use std::backtrace::{self, PrintFormat};
+
+use crate::acs::ACSEnclave;
+
+register_ecall_handler!(
+ type ECallCommand,
+ (ECallCommand::ServeConnection, ServeConnectionInput, ServeConnectionOutput),
+ (ECallCommand::InitEnclave, InitEnclaveInput, InitEnclaveOutput),
+ (ECallCommand::FinalizeEnclave, FinalizeEnclaveInput, FinalizeEnclaveOutput),
+);
+
+extern "C" {
+ fn acs_setup_model(model_text: *const c_char) -> i32;
+}
+
+#[handle_ecall]
+fn handle_serve_connection(args: &ServeConnectionInput) -> Result<ServeConnectionOutput> {
+ debug!("Enclave [ACS]: Serve Connection.");
+
+ let server_instance = ACSEnclave::default();
+ let acs_config = config::Internal::acs();
+ assert_eq!(args.port, acs_config.addr.port());
+
+ let enclave_attr = match acs_config.inbound_desc {
+ config::InboundDesc::Sgx(enclave_attr) => enclave_attr,
+ _ => unreachable!(),
+ };
+
+ let config = PipeConfig {
+ fd: args.socket_fd,
+ retry: 0,
+ client_attr: Some(enclave_attr),
+ };
+
+ let mut server = match Pipe::start(config) {
+ Ok(s) => s,
+ Err(e) => {
+ error!("Start Pipe failed: {}", e);
+ return Ok(ServeConnectionOutput::default());
+ }
+ };
+
+ let _ = server.serve(server_instance);
+
+ // We discard all enclave internal errors here.
+ Ok(ServeConnectionOutput::default())
+}
+
+const MODEL_TEXT: &'static str = include_str!("../../model.conf");
+
+#[handle_ecall]
+fn handle_init_enclave(_args: &InitEnclaveInput) -> Result<InitEnclaveOutput> {
+ debug!("Enclave [ACS]: Initializing...");
+
+ env_logger::init();
+ let _ = backtrace::enable_backtrace(
+ concat!(include_str!("../../pkg_name"), ".enclave.signed.so"),
+ PrintFormat::Full,
+ );
+ mesatee_core::rpc::sgx::prelude();
+
+ eprintln!("setting up acs model");
+
+ let ec = unsafe { acs_setup_model(CString::new(MODEL_TEXT).unwrap().as_ptr()) };
+
+ if ec != 0 {
+ Err(Error::from(ErrorKind::MesaPyError))
+ } else {
+ Ok(InitEnclaveOutput::default())
+ }
+}
+
+#[handle_ecall]
+fn handle_finalize_enclave(_args: &FinalizeEnclaveInput) -> Result<FinalizeEnclaveOutput> {
+ #[cfg(feature = "cov")]
+ sgx_cov::cov_writeout();
+
+ debug!("Enclave [ACS]: Finalized.");
+ Ok(FinalizeEnclaveOutput::default())
+}
diff --git a/tests/functional_test.sh b/tests/functional_test.sh
index a825c23..963c60f 100755
--- a/tests/functional_test.sh
+++ b/tests/functional_test.sh
@@ -15,6 +15,10 @@ if lsof -i :5066; then
echo "[-] port 5066 is in use"
exit 1
fi
+if lsof -i :5077; then
+ echo "[-] port 5066 is in use"
+ exit 1
+fi
if lsof -i :5065; then
echo "[-] port 5065 is in use"
exit 1
@@ -27,10 +31,6 @@ if lsof -i :5555; then
echo "[-] port 5555 is in use"
exit 1
fi
-if lsof -i :5555; then
- echo "[-] port 5555 is in use"
- exit 1
-fi
if lsof -i :3444; then
echo "[-] port 3444 is in use"
exit 1
@@ -41,6 +41,7 @@ fi
./tdfs 2>&1 | tee tdfs.log &
./tms 2>&1 | tee tms.log &
./fns 2>&1 | tee fns.log &
+./acs 2>&1 | tee acs.log &
wait_service() {
name=$1
@@ -61,6 +62,7 @@ wait_service tdfs 5065 30
wait_service tms 5554 30
wait_service tms 5555 30
wait_service fns 3444 30
+wait_service acs 5077 30
./functional_test 2>&1 | tee functional_test.log
exit ${PIPESTATUS[0]}
diff --git a/tests/functional_test/sgx_trusted_lib/Cargo.toml b/tests/functional_test/sgx_trusted_lib/Cargo.toml
index 27f78b5..65b052d 100644
--- a/tests/functional_test/sgx_trusted_lib/Cargo.toml
+++ b/tests/functional_test/sgx_trusted_lib/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["staticlib"]
[features]
default = []
-mesalock_sgx = ["sgx_tstd", "sgx_tunittest", "mesatee_core/mesalock_sgx", "kms_client/mesalock_sgx", "tdfs_internal_client/mesalock_sgx", "tms_internal_client/mesalock_sgx", "tms_internal_proto/mesalock_sgx", "protected_fs_rs/mesalock_sgx"]
+mesalock_sgx = ["sgx_tstd", "sgx_tunittest", "mesatee_core/mesalock_sgx", "kms_client/mesalock_sgx", "acs_client/mesalock_sgx", "tdfs_internal_client/mesalock_sgx", "tms_internal_client/mesalock_sgx", "tms_internal_proto/mesalock_sgx", "protected_fs_rs/mesalock_sgx"]
cov = ["sgx_cov"]
[dependencies]
@@ -22,6 +22,7 @@ env_logger = { version = "0.7.1" }
rand = { version = "0.7" }
kms_client = { path = "../../../mesatee_services/kms/client", optional = true }
+acs_client = { path = "../../../mesatee_services/acs/client", optional = true }
tdfs_internal_client = { path = "../../../mesatee_services/tdfs/internal/client", optional = true }
tms_internal_client = { path = "../../../mesatee_services/tms/internal/client", optional = true }
tms_internal_proto = { path = "../../../mesatee_services/tms/internal/proto", optional = true }
diff --git a/tests/functional_test/sgx_trusted_lib/src/sgx.rs b/tests/functional_test/sgx_trusted_lib/src/sgx.rs
index 1fc2faa..3069e7e 100644
--- a/tests/functional_test/sgx_trusted_lib/src/sgx.rs
+++ b/tests/functional_test/sgx_trusted_lib/src/sgx.rs
@@ -47,6 +47,7 @@ fn handle_run_functional_test(_args: &RunFunctionalTestInput) -> Result<RunFunct
tests::tms_test::update_task_result,
tests::tms_test::update_private_result,
tests::tms_test::update_status,
+ tests::acs_test::access_control_model,
);
Ok(RunFunctionalTestOutput::new(nfailed))
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/acs_test.rs b/tests/functional_test/sgx_trusted_lib/src/tests/acs_test.rs
new file mode 100644
index 0000000..51d2d5c
--- /dev/null
+++ b/tests/functional_test/sgx_trusted_lib/src/tests/acs_test.rs
@@ -0,0 +1,267 @@
+// Copyright 2019 MesaTEE Authors
+//
+// 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.
+
+use super::common_setup::setup_acs_internal_client;
+
+use std::collections::HashSet;
+use std::string::ToString;
+
+const FUSION_TASK: &'static str = "data_fusion";
+
+const FUSION_TASK_PARTY_1: &'static str = "usr1";
+const FUSION_TASK_PARTY_2: &'static str = "usr2";
+
+const FUSION_TASK_DATA_1: &'static str = "data1";
+const FUSION_TASK_DATA_2: &'static str = "data2";
+
+const FUSION_TASK_SCRIPT: &'static str = "fusion_script";
+const FUSION_TASK_SCRIPT_WRITER: &'static str = "usr3";
+const PUBLIC_SCRIPT: &'static str = "public_script";
+const PUBLIC_SCRIPT_WRITER: &'static str = "usr4";
+
+const IRRELEVANT_TASK: &'static str = "task_irrelevant";
+const IRRELEVANT_PARTY: &'static str = "usr_irrelevant";
+const IRRELEVANT_DATA: &'static str = "data_irrelevant";
+
+pub fn access_control_model() {
+ trace!("Test ACS: access control model.");
+ let mut client = setup_acs_internal_client();
+
+ let mut participants = HashSet::new();
+ participants.insert(FUSION_TASK_PARTY_1.to_string());
+ participants.insert(FUSION_TASK_PARTY_2.to_string());
+ participants.insert(FUSION_TASK_SCRIPT_WRITER.to_string());
+
+ client.announce_task_creation(
+ FUSION_TASK.to_string(),
+ FUSION_TASK_PARTY_1.to_string(),
+ &participants,
+ ).expect("fusion task creation announcement failed");
+
+ client.announce_data_creation(
+ FUSION_TASK_DATA_1.to_string(),
+ FUSION_TASK_PARTY_1.to_string(),
+ ).expect("fusion data n1 creation announcement failed");
+
+ client.announce_data_creation(
+ FUSION_TASK_DATA_2.to_string(),
+ FUSION_TASK_PARTY_2.to_string(),
+ ).expect("fusion data 2 creation announcement failed");
+
+ client.announce_data_creation(
+ IRRELEVANT_DATA.to_string(),
+ IRRELEVANT_PARTY.to_string(),
+ ).expect("irrelevant data creation announcement failed");
+
+ client.announce_script_creation(
+ FUSION_TASK_SCRIPT.to_string(),
+ FUSION_TASK_SCRIPT_WRITER.to_string(),
+ false,
+ ).expect("fusion script creation announcement failed");
+
+ client.announce_script_creation(
+ PUBLIC_SCRIPT.to_string(),
+ PUBLIC_SCRIPT_WRITER.to_string(),
+ true,
+ ).expect("public script creation announcement failed");
+
+ let mut participants = HashSet::new();
+
+ assert_eq!(
+ client.enforce_task_launch(
+ FUSION_TASK.to_string(),
+ participants.clone(),
+ ).unwrap(),
+ false,
+ );
+
+ participants.insert(FUSION_TASK_PARTY_1.to_string());
+
+ assert_eq!(
+ client.enforce_task_launch(
+ FUSION_TASK.to_string(),
+ participants.clone(),
+ ).unwrap(),
+ false,
+ );
+
+ participants.insert(FUSION_TASK_PARTY_2.to_string());
+
+ assert_eq!(
+ client.enforce_task_launch(
+ FUSION_TASK.to_string(),
+ participants.clone(),
+ ).unwrap(),
+ false,
+ );
+
+ participants.insert(FUSION_TASK_SCRIPT_WRITER.to_string());
+
+ assert_eq!(
+ client.enforce_task_launch(
+ FUSION_TASK.to_string(),
+ participants.clone(),
+ ).unwrap(),
+ true,
+ );
+
+ // Load fusion script
+ assert_eq!(
+ client.enforce_script_access(
+ FUSION_TASK.to_string(),
+ FUSION_TASK_SCRIPT.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ // Load public script
+ assert_eq!(
+ client.enforce_script_access(
+ FUSION_TASK.to_string(),
+ PUBLIC_SCRIPT.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ // Read data1
+ assert_eq!(
+ client.enforce_data_access(
+ FUSION_TASK.to_string(),
+ FUSION_TASK_DATA_1.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ // Read data2
+ assert_eq!(
+ client.enforce_data_access(
+ FUSION_TASK.to_string(),
+ FUSION_TASK_DATA_2.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ let mut participants = HashSet::new();
+
+ participants.insert(IRRELEVANT_PARTY.to_string());
+ participants.insert(FUSION_TASK_PARTY_2.to_string());
+
+ client.announce_task_creation(
+ IRRELEVANT_TASK.to_string(),
+ IRRELEVANT_PARTY.to_string(),
+ &participants,
+ ).expect("irrelevant task creation announcement failed");
+
+ // Launch irrelevant task
+ assert_eq!(
+ client.enforce_task_launch(
+ IRRELEVANT_TASK.to_string(),
+ participants,
+ ).unwrap(),
+ true,
+ );
+
+ // Load fusion script; deny
+ assert_eq!(
+ client.enforce_script_access(
+ IRRELEVANT_TASK.to_string(),
+ FUSION_TASK_SCRIPT.to_string(),
+ ).unwrap(),
+ false,
+ );
+
+ // Load public script; allow
+ assert_eq!(
+ client.enforce_script_access(
+ IRRELEVANT_TASK.to_string(),
+ PUBLIC_SCRIPT.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ // Read data1; deny
+ assert_eq!(
+ client.enforce_data_access(
+ IRRELEVANT_TASK.to_string(),
+ FUSION_TASK_DATA_1.to_string(),
+ ).unwrap(),
+ false,
+ );
+
+ // Read data2; allow
+ assert_eq!(
+ client.enforce_data_access(
+ IRRELEVANT_TASK.to_string(),
+ FUSION_TASK_DATA_2.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ assert_eq!(
+ client.enforce_data_deletion(
+ FUSION_TASK_PARTY_1.to_string(),
+ FUSION_TASK_DATA_1.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ assert_eq!(
+ client.enforce_data_deletion(
+ FUSION_TASK_PARTY_2.to_string(),
+ FUSION_TASK_DATA_2.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ assert_eq!(
+ client.enforce_data_deletion(
+ FUSION_TASK_PARTY_1.to_string(),
+ FUSION_TASK_DATA_2.to_string(),
+ ).unwrap(),
+ false,
+ );
+
+ assert_eq!(
+ client.enforce_script_deletion(
+ FUSION_TASK_PARTY_1.to_string(),
+ FUSION_TASK_SCRIPT.to_string(),
+ ).unwrap(),
+ false,
+ );
+
+ assert_eq!(
+ client.enforce_script_deletion(
+ FUSION_TASK_SCRIPT_WRITER.to_string(),
+ FUSION_TASK_SCRIPT.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+ assert_eq!(
+ client.enforce_script_deletion(
+ IRRELEVANT_PARTY.to_string(),
+ PUBLIC_SCRIPT.to_string(),
+ ).unwrap(),
+ false,
+ );
+
+ assert_eq!(
+ client.enforce_script_deletion(
+ PUBLIC_SCRIPT_WRITER.to_string(),
+ PUBLIC_SCRIPT.to_string(),
+ ).unwrap(),
+ true,
+ );
+
+}
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs b/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs
index b6fa952..92244f3 100644
--- a/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs
+++ b/tests/functional_test/sgx_trusted_lib/src/tests/common_setup.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use kms_client::KMSClient;
+use acs_client::ACSClient;
use mesatee_core::config;
use tdfs_internal_client::TDFSClient;
use tms_internal_client::TMSClient;
@@ -21,6 +22,11 @@ pub(crate) fn setup_kms_internal_client() -> KMSClient {
KMSClient::new(target).unwrap()
}
+pub(crate) fn setup_acs_internal_client() -> ACSClient {
+ let target = config::Internal::target_acs();
+ ACSClient::new(target).unwrap()
+}
+
pub(crate) fn setup_tdfs_internal_client() -> TDFSClient {
let target = config::Internal::target_tdfs();
TDFSClient::new(target).unwrap()
@@ -30,3 +36,4 @@ pub(crate) fn setup_tms_internal_client() -> TMSClient {
let target = config::Internal::target_tms();
TMSClient::new(target).unwrap()
}
+
diff --git a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs b/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
index dde9bb0..d8b9735 100644
--- a/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
+++ b/tests/functional_test/sgx_trusted_lib/src/tests/mod.rs
@@ -17,3 +17,4 @@ pub mod kms_test;
pub mod protected_fs_test;
pub mod tdfs_test;
pub mod tms_test;
+pub mod acs_test;
diff --git a/third_party/mesapy b/third_party/mesapy
new file mode 160000
index 0000000..544d70a
--- /dev/null
+++ b/third_party/mesapy
@@ -0,0 +1 @@
+Subproject commit 544d70a041643eed8a0f362e9746facb254381af
diff --git a/toolchain_deps/Cargo.sgx_trusted_lib.toml b/toolchain_deps/Cargo.sgx_trusted_lib.toml
index 6124d15..f53d6db 100644
--- a/toolchain_deps/Cargo.sgx_trusted_lib.toml
+++ b/toolchain_deps/Cargo.sgx_trusted_lib.toml
@@ -5,6 +5,7 @@ members = [
"mesatee_services/fns/sgx_trusted_lib",
"mesatee_services/tdfs/sgx_trusted_lib",
"mesatee_services/tms/sgx_trusted_lib",
+ "mesatee_services/acs/sgx_trusted_lib",
"tests/functional_test/sgx_trusted_lib",
]
diff --git a/toolchain_deps/Cargo.sgx_untrusted_app.toml b/toolchain_deps/Cargo.sgx_untrusted_app.toml
index c734558..8c8f2f2 100644
--- a/toolchain_deps/Cargo.sgx_untrusted_app.toml
+++ b/toolchain_deps/Cargo.sgx_untrusted_app.toml
@@ -5,6 +5,7 @@ members = [
"mesatee_services/fns/sgx_app",
"mesatee_services/tdfs/sgx_app",
"mesatee_services/tms/sgx_app",
+ "mesatee_services/acs/sgx_app",
"tests/functional_test/sgx_app",
]
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@mesatee.apache.org
For additional commands, e-mail: commits-help@mesatee.apache.org