diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 693e3e3b..eae52e44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,10 +117,10 @@ jobs: source environment # Build OP-TEE Rust examples for Arm 64-bit both host and TA - make -j`nproc` + make -j2 # Build project - (cd projects/web3/eth_wallet && make -j`nproc`) + (cd projects/web3/eth_wallet && make) - name: Run tests for Arm 64-bit both host and TA run: | export STD=y @@ -151,10 +151,10 @@ jobs: source environment # Build OP-TEE Rust examples for Arm 64-bit both host and TA - make -j`nproc` + make -j2 # Build project - (cd projects/web3/eth_wallet && make -j`nproc`) + (cd projects/web3/eth_wallet && make) - name: Run tests for Arm 32-bit both host and TA run: | export ARCH_TA=arm diff --git a/ci/ci.sh b/ci/ci.sh index e10916f8..75567574 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -45,6 +45,7 @@ if [ "$STD" ]; then ./test_tls_client.sh ./test_tls_server.sh ./test_eth_wallet.sh + ./test_secure_db_abstraction.sh fi popd diff --git a/examples/secure_db_abstraction-rs/Makefile b/examples/secure_db_abstraction-rs/Makefile new file mode 100644 index 00000000..a7a3dec8 --- /dev/null +++ b/examples/secure_db_abstraction-rs/Makefile @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# If _HOST or _TA specific compiler/target are not specified, then use common +# compiler/target for both +CROSS_COMPILE_HOST ?= aarch64-linux-gnu- +CROSS_COMPILE_TA ?= aarch64-linux-gnu- +TARGET_HOST ?= aarch64-unknown-linux-gnu +TARGET_TA ?= aarch64-unknown-linux-gnu + +.PHONY: host ta all clean + +all: host ta + +host: + $(q)make -C host TARGET=$(TARGET_HOST) \ + CROSS_COMPILE=$(CROSS_COMPILE_HOST) + +ta: + $(q)make -C ta TARGET=$(TARGET_TA) \ + CROSS_COMPILE=$(CROSS_COMPILE_TA) + +clean: + $(q)make -C host clean + $(q)make -C ta clean diff --git a/examples/secure_db_abstraction-rs/host/Cargo.toml b/examples/secure_db_abstraction-rs/host/Cargo.toml new file mode 100644 index 00000000..c199ce8c --- /dev/null +++ b/examples/secure_db_abstraction-rs/host/Cargo.toml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +[package] +name = "secure_db_abstraction-rs" +version = "0.1.0" +authors = ["Teaclave Contributors "] +license = "Apache-2.0" +repository = "https://github.com/apache/incubator-teaclave-trustzone-sdk.git" +description = "An example of Rust OP-TEE TrustZone SDK." +edition = "2018" + +[dependencies] +proto = { path = "../proto" } +optee-teec = { path = "../../../optee-teec" } + +[profile.release] +lto = true diff --git a/examples/secure_db_abstraction-rs/host/Makefile b/examples/secure_db_abstraction-rs/host/Makefile new file mode 100644 index 00000000..c8b90ce1 --- /dev/null +++ b/examples/secure_db_abstraction-rs/host/Makefile @@ -0,0 +1,43 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# STD-ONLY example + +NAME := secure_db_abstraction-rs + +TARGET ?= aarch64-unknown-linux-gnu +CROSS_COMPILE ?= aarch64-linux-gnu- +OBJCOPY := $(CROSS_COMPILE)objcopy +LINKER_CFG := target.$(TARGET).linker=\"$(CROSS_COMPILE)gcc\" + +OUT_DIR := $(CURDIR)/target/$(TARGET)/release + +ifeq ($(STD),) +all: + @echo "Please \`export STD=y\` then rerun \`source environment\` to build the STD version" +else +all: host strip +endif + +host: + @cargo build --target $(TARGET_HOST) --release --config $(LINKER_CFG) + +strip: host + @$(OBJCOPY) --strip-unneeded $(OUT_DIR)/$(NAME) $(OUT_DIR)/$(NAME) + +clean: + @cargo clean diff --git a/examples/secure_db_abstraction-rs/host/src/main.rs b/examples/secure_db_abstraction-rs/host/src/main.rs new file mode 100644 index 00000000..1647efd4 --- /dev/null +++ b/examples/secure_db_abstraction-rs/host/src/main.rs @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 optee_teec::{Context, ErrorKind, Operation, ParamNone, Uuid}; +use proto::{Command, UUID}; + +fn main() -> optee_teec::Result<()> { + let mut ctx = Context::new()?; + let uuid = + Uuid::parse_str(UUID).map_err(|_| optee_teec::Error::from(ErrorKind::BadParameters))?; + let mut session = ctx.open_session(uuid)?; + let mut operation = Operation::new(0, ParamNone, ParamNone, ParamNone, ParamNone); + + // Nothing to send, just invoke the Test command + session.invoke_command(Command::Test as u32, &mut operation)?; + println!("Success"); + Ok(()) +} diff --git a/examples/secure_db_abstraction-rs/proto/Cargo.toml b/examples/secure_db_abstraction-rs/proto/Cargo.toml new file mode 100644 index 00000000..e30aef83 --- /dev/null +++ b/examples/secure_db_abstraction-rs/proto/Cargo.toml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +[package] +name = "proto" +version = "0.1.0" +authors = ["Teaclave Contributors "] +license = "Apache-2.0" +repository = "https://github.com/apache/incubator-teaclave-trustzone-sdk.git" +description = "Data structures and functions shared by host and TA." +edition = "2018" + +[dependencies] +num_enum = { version = "0.7.3", default-features = false } diff --git a/examples/secure_db_abstraction-rs/proto/src/lib.rs b/examples/secure_db_abstraction-rs/proto/src/lib.rs new file mode 100644 index 00000000..76bf57fe --- /dev/null +++ b/examples/secure_db_abstraction-rs/proto/src/lib.rs @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 num_enum::{FromPrimitive, IntoPrimitive}; + +#[derive(FromPrimitive, IntoPrimitive)] +#[repr(u32)] +pub enum Command { + Test, + #[default] + Unknown, +} + +// If Uuid::parse_str() returns an InvalidLength error, there may be an extra +// newline in your uuid.txt file. You can remove it by running +// `truncate -s 36 uuid.txt`. +pub const UUID: &str = &include_str!("../../uuid.txt"); diff --git a/examples/secure_db_abstraction-rs/ta/Cargo.toml b/examples/secure_db_abstraction-rs/ta/Cargo.toml new file mode 100644 index 00000000..40e2e9d2 --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/Cargo.toml @@ -0,0 +1,42 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +[package] +name = "ta" +version = "0.1.0" +authors = ["Teaclave Contributors "] +license = "Apache-2.0" +repository = "https://github.com/apache/incubator-teaclave-trustzone-sdk.git" +description = "An example of Rust OP-TEE TrustZone SDK." +edition = "2018" + +[dependencies] +proto = { path = "../proto" } +optee-utee-sys = { path = "../../../optee-utee/optee-utee-sys" } +optee-utee = { path = "../../../optee-utee" } +bincode = "1.3.3" +anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } + +[build-dependencies] +proto = { path = "../proto" } +optee-utee-build = { path = "../../../optee-utee-build" } + +[profile.release] +panic = "abort" +lto = true +opt-level = 1 diff --git a/examples/secure_db_abstraction-rs/ta/Makefile b/examples/secure_db_abstraction-rs/ta/Makefile new file mode 100644 index 00000000..42d92360 --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/Makefile @@ -0,0 +1,50 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# STD-ONLY example + +UUID ?= $(shell cat "../uuid.txt") + +TARGET ?= aarch64-unknown-linux-gnu +CROSS_COMPILE ?= aarch64-linux-gnu- +OBJCOPY := $(CROSS_COMPILE)objcopy +# Configure the linker to use GCC, which works on both cross-compilation and ARM machines +LINKER_CFG := target.$(TARGET).linker=\"$(CROSS_COMPILE)gcc\" + +TA_SIGN_KEY ?= $(TA_DEV_KIT_DIR)/keys/default_ta.pem +SIGN := $(TA_DEV_KIT_DIR)/scripts/sign_encrypt.py +OUT_DIR := $(CURDIR)/target/$(TARGET)/release + +ifeq ($(STD),) +all: + @echo "Please \`export STD=y\` then rerun \`source environment\` to build the STD version" +else +all: ta strip sign +endif + +ta: + @xargo build --target $(TARGET) --release --config $(LINKER_CFG) + +strip: ta + @$(OBJCOPY) --strip-unneeded $(OUT_DIR)/ta $(OUT_DIR)/stripped_ta + +sign: strip + @$(SIGN) --uuid $(UUID) --key $(TA_SIGN_KEY) --in $(OUT_DIR)/stripped_ta --out $(OUT_DIR)/$(UUID).ta + @echo "SIGN => ${UUID}" + +clean: + @cargo clean diff --git a/examples/secure_db_abstraction-rs/ta/Xargo.toml b/examples/secure_db_abstraction-rs/ta/Xargo.toml new file mode 100644 index 00000000..1b1a113e --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/Xargo.toml @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +[dependencies.std] +path = "../../../rust/rust/library/std" + +[patch.crates-io] +libc = { path = "../../../rust/libc" } +rustc-std-workspace-core = { path = "../../../rust/rust/library/rustc-std-workspace-core" } +rustc-std-workspace-alloc = { path = "../../../rust/rust/library/rustc-std-workspace-alloc" } diff --git a/examples/secure_db_abstraction-rs/ta/build.rs b/examples/secure_db_abstraction-rs/ta/build.rs new file mode 100644 index 00000000..34ae5faa --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/build.rs @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 optee_utee_build::{Error, RustEdition, TaConfig}; +use proto; + +fn main() -> Result<(), Error> { + let ta_config = TaConfig::new_default_with_cargo_env(proto::UUID)?; + optee_utee_build::build(RustEdition::Before2024, ta_config) +} diff --git a/examples/secure_db_abstraction-rs/ta/src/main.rs b/examples/secure_db_abstraction-rs/ta/src/main.rs new file mode 100644 index 00000000..adc61bf1 --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/src/main.rs @@ -0,0 +1,120 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#![no_main] + +mod secure_db; + +extern crate alloc; + +use alloc::vec; +use optee_utee::{ + ta_close_session, ta_create, ta_destroy, ta_invoke_command, ta_open_session, trace_println, +}; +use optee_utee::{Error, ErrorKind, Parameters, Result}; +use proto::Command; +use secure_db::{SecureStorageClient, Storable}; +use serde::{Deserialize, Serialize}; + +#[ta_create] +fn create() -> Result<()> { + trace_println!("[+] TA create"); + Ok(()) +} + +#[ta_open_session] +fn open_session(_params: &mut Parameters) -> Result<()> { + trace_println!("[+] TA open session"); + Ok(()) +} + +#[ta_close_session] +fn close_session() { + trace_println!("[+] TA close session"); +} + +#[ta_destroy] +fn destroy() { + trace_println!("[+] TA destroy"); +} + +#[ta_invoke_command] +fn invoke_command(cmd_id: u32, _params: &mut Parameters) -> Result<()> { + trace_println!("[+] TA invoke command"); + match Command::from(cmd_id) { + Command::Test => match test() { + Ok(_) => { + trace_println!("[+] Test passed"); + Ok(()) + } + Err(e) => { + trace_println!("[-] Test failed: {:?}", e); + Err(Error::new(ErrorKind::Generic)) + } + }, + _ => { + return Err(Error::new(ErrorKind::NotSupported)); + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct ExampleData { + pub id: String, + pub data: Vec, +} + +// Any structure that implements Storable can be stored in the secure db. +// Any Key type can be used as unique id as long as it implements +// TryFrom + Into + Clone +impl Storable for ExampleData { + type Key = String; + + fn unique_id(&self) -> Self::Key { + self.id.clone() + } +} + +pub fn test() -> anyhow::Result<()> { + // Assume this is the data that we want to store + let example_data = ExampleData { + id: "example_data".to_string(), + data: vec![1, 2, 3, 4, 5], + }; + + // Initialize secure storage db client with a db name + let db_client = SecureStorageClient::open("secure_db")?; + + // Now, we can do common db interactions using the db_client: + // Store data in db using put() + db_client.put(&example_data)?; + // Load data from db using get() + let loaded_example_data = db_client.get::(&example_data.id)?; + anyhow::ensure!( + loaded_example_data == example_data, + "Loaded example_data is not equal to the original example_data" + ); + // List all entries in db + let entries = db_client.list_entries::()?; + trace_println!("Entries: {:?}", entries); + // Delete entry from db + db_client.delete_entry::(&example_data.id)?; + + Ok(()) +} + +include!(concat!(env!("OUT_DIR"), "/user_ta_header.rs")); diff --git a/examples/secure_db_abstraction-rs/ta/src/secure_db/backend.rs b/examples/secure_db_abstraction-rs/ta/src/secure_db/backend.rs new file mode 100644 index 00000000..ab40dcd8 --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/src/secure_db/backend.rs @@ -0,0 +1,86 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 anyhow::{anyhow, bail, Result}; +use optee_utee::{DataFlag, ObjectStorageConstants, PersistentObject}; + +// Wrapper functions for OP-TEE raw API + +pub fn save_in_secure_storage(obj_id: &[u8], data: &[u8]) -> Result<()> { + let obj_data_flag = DataFlag::ACCESS_READ + | DataFlag::ACCESS_WRITE + | DataFlag::ACCESS_WRITE_META + | DataFlag::OVERWRITE; + + PersistentObject::create( + ObjectStorageConstants::Private, + obj_id, + obj_data_flag, + None, + data, + ) + .map_err(|e| anyhow!("[-] {:?}: failed to create object: {:?}", &obj_id, e))?; + + Ok(()) +} + +pub fn load_from_secure_storage(obj_id: &[u8]) -> Result>> { + match PersistentObject::open( + ObjectStorageConstants::Private, + obj_id, + DataFlag::ACCESS_READ | DataFlag::SHARE_READ, + ) { + Err(e) => match e.kind() { + optee_utee::ErrorKind::ItemNotFound => { + return Ok(None); + } + _ => { + bail!("[-] {:?}: failed to open object: {:?}", &obj_id, e); + } + }, + + Ok(object) => { + let obj_info = object.info()?; + let mut buf = vec![0u8; obj_info.data_size() as usize]; + + let read_bytes = object.read(&mut buf)?; + if read_bytes != obj_info.data_size() as u32 { + bail!("[-] {:?}: failed to read data", &obj_id); + } + + return Ok(Some(buf)); + } + } +} + +pub fn delete_from_secure_storage(obj_id: &[u8]) -> Result<()> { + match PersistentObject::open( + ObjectStorageConstants::Private, + &obj_id, + DataFlag::ACCESS_READ | DataFlag::ACCESS_WRITE_META, + ) { + Err(e) => { + bail!("[-] {:?}: failed to open object: {:?}", &obj_id, e); + } + + Ok(mut object) => { + object.close_and_delete()?; + std::mem::forget(object); + return Ok(()); + } + } +} diff --git a/examples/secure_db_abstraction-rs/ta/src/secure_db/client.rs b/examples/secure_db_abstraction-rs/ta/src/secure_db/client.rs new file mode 100644 index 00000000..d6d649e8 --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/src/secure_db/client.rs @@ -0,0 +1,102 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 crate::secure_db::SecureStorageDb; +use crate::Storable; +use anyhow::{anyhow, Result}; +use std::{ + collections::HashMap, + convert::TryFrom, + hash::Hash, + sync::{Arc, RwLock}, +}; + +// SecureStorageClient is a client to interact with SecureStorageDb. +// Bound operations to Structure that implements Storable trait. + +pub struct SecureStorageClient { + db: Arc>, +} + +impl SecureStorageClient { + pub fn open(db_name: &str) -> Result { + Ok(Self { + db: Arc::new(RwLock::new(SecureStorageDb::open(db_name.to_string())?)), + }) + } + + pub fn get(&self, key: &V::Key) -> Result + where + V: Storable + serde::de::DeserializeOwned, + V::Key: Into + Clone, + { + let key: String = key.clone().into(); + let storage_key = V::concat_key(&key); + let value = self + .db + .read() + .map_err(|_| anyhow!("Failed to acquire read lock"))? + .get(&storage_key)?; + Ok(bincode::deserialize(&value)?) + } + + pub fn put(&self, value: &V) -> Result<()> + where + V: Storable + serde::Serialize, + { + let key = value.storage_key(); + let value = bincode::serialize(value)?; + self.db + .write() + .map_err(|_| anyhow!("Failed to acquire write lock"))? + .put(key, value)?; + Ok(()) + } + + pub fn delete_entry(&self, key: &V::Key) -> Result<()> + where + V: Storable, + V::Key: Into + Clone, + { + let key: String = key.clone().into(); + let storage_key = V::concat_key(&key); + self.db + .write() + .map_err(|_| anyhow!("Failed to acquire write lock"))? + .delete(&storage_key)?; + Ok(()) + } + + pub fn list_entries(&self) -> Result> + where + V: Storable + serde::de::DeserializeOwned, + V::Key: TryFrom + Eq + Hash, + { + let map = self + .db + .read() + .map_err(|_| anyhow!("Failed to acquire read lock"))? + .list_entries_with_prefix(V::table_name())?; + let mut result = HashMap::new(); + for (_k, v) in map { + let value: V = bincode::deserialize(&v)?; + let key = value.unique_id(); + result.insert(key, value); + } + Ok(result) + } +} diff --git a/examples/secure_db_abstraction-rs/ta/src/secure_db/db.rs b/examples/secure_db_abstraction-rs/ta/src/secure_db/db.rs new file mode 100644 index 00000000..580601a5 --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/src/secure_db/db.rs @@ -0,0 +1,113 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 crate::secure_db::{ + delete_from_secure_storage, load_from_secure_storage, save_in_secure_storage, +}; +use anyhow::{bail, ensure, Result}; +use std::collections::{HashMap, HashSet}; + +// SecureStorageDb is a key-value storage for TA to easily store and retrieve data. +// First we store the key list in the secure storage, named as db_name. +// Then we store the each key-value pairs in the secure storage. + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SecureStorageDb { + name: String, + key_list: HashSet, +} + +impl SecureStorageDb { + pub fn open(name: String) -> Result { + match load_from_secure_storage(name.as_bytes())? { + Some(data) => { + let key_list = bincode::deserialize(&data)?; + Ok(Self { name, key_list }) + } + None => { + // create new db + Ok(Self { + name, + key_list: HashSet::new(), + }) + } + } + } + + pub fn put(&mut self, key: String, value: Vec) -> Result<()> { + match save_in_secure_storage(key.as_bytes(), &value) { + Ok(_) => { + self.key_list.insert(key); + self.store_key_list()?; + } + Err(e) => { + bail!("[+] SecureStorage::insert(): save error: {}", e); + } + }; + Ok(()) + } + + pub fn get(&self, key: &str) -> Result> { + ensure!(self.key_list.contains(key), "Key not found in key list"); + match load_from_secure_storage(key.as_bytes()) { + Ok(Some(data)) => Ok(data), + Ok(None) => bail!("[+] SecureStorage::get(): object not found in db"), + Err(e) => { + bail!("[+] SecureStorage::get(): load error: {}", e); + } + } + } + + pub fn delete(&mut self, key: &str) -> Result<()> { + // ensure key must exist + ensure!(self.key_list.contains(key), "Key not found in key list"); + match delete_from_secure_storage(key.as_bytes()) { + Ok(_) => { + self.key_list.remove(key); + self.store_key_list()?; + } + Err(e) => { + bail!("[+] SecureStorage::delete(): delete error: {}", e); + } + }; + Ok(()) + } + + pub fn clear(&mut self) -> Result<()> { + for key in self.key_list.clone() { + self.delete(&key)?; + } + Ok(()) + } + + pub fn list_entries_with_prefix(&self, prefix: &str) -> Result>> { + let mut result = HashMap::new(); + for key in &self.key_list { + if key.starts_with(prefix) { + let value = self.get(key)?; + result.insert(key.clone(), value); + } + } + Ok(result) + } + + fn store_key_list(&self) -> Result<()> { + let key_list = bincode::serialize(&self.key_list)?; + save_in_secure_storage(self.name.as_bytes(), &key_list)?; + Ok(()) + } +} diff --git a/examples/secure_db_abstraction-rs/ta/src/secure_db/mod.rs b/examples/secure_db_abstraction-rs/ta/src/secure_db/mod.rs new file mode 100644 index 00000000..7b34551a --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/src/secure_db/mod.rs @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +mod backend; +pub use backend::*; +mod client; +pub use client::*; +mod db; +pub use db::*; +mod storable; +pub use storable::*; diff --git a/examples/secure_db_abstraction-rs/ta/src/secure_db/storable.rs b/examples/secure_db_abstraction-rs/ta/src/secure_db/storable.rs new file mode 100644 index 00000000..afe063b9 --- /dev/null +++ b/examples/secure_db_abstraction-rs/ta/src/secure_db/storable.rs @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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::{convert::TryFrom, hash::Hash}; + +// For each key-value data, the storage key is "$TABLE_NAME#$KEY" +// For example, if we store the Data whose type is Structure named +// ExampleData, with id "123", +// the storage key will be "ExampleData#123" + +const CONCAT: &str = "#"; + +pub trait Storable { + type Key: Into + Clone + TryFrom + Eq + Hash; // Associated type `Key` + + fn unique_id(&self) -> Self::Key; + + fn table_name() -> &'static str { + // keeps the last part of the path + std::any::type_name::() + .split("::") + .last() + .unwrap_or("WRONG_TABLE_NAME") + } + + fn storage_key(&self) -> String { + format!( + "{}{}{}", + Self::table_name(), + CONCAT, + Into::::into(self.unique_id()) + ) + } + + fn concat_key(key: &str) -> String { + format!("{}{}{}", Self::table_name(), CONCAT, key) + } +} diff --git a/examples/secure_db_abstraction-rs/uuid.txt b/examples/secure_db_abstraction-rs/uuid.txt new file mode 100644 index 00000000..84672542 --- /dev/null +++ b/examples/secure_db_abstraction-rs/uuid.txt @@ -0,0 +1 @@ +e55291e1-521c-4dca-aa24-51e34ab32ad9 \ No newline at end of file diff --git a/tests/test_secure_db_abstraction.sh b/tests/test_secure_db_abstraction.sh new file mode 100755 index 00000000..6bb0637e --- /dev/null +++ b/tests/test_secure_db_abstraction.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +set -xe + +# Include base script +source setup.sh + +# Copy TA and host binary +cp ../examples/secure_db_abstraction-rs/ta/target/$TARGET_TA/release/*.ta shared +cp ../examples/secure_db_abstraction-rs/host/target/$TARGET_HOST/release/secure_db_abstraction-rs shared + +# Run script specific commands in QEMU +run_in_qemu "cp *.ta /lib/optee_armtz/\n" +run_in_qemu "./secure_db_abstraction-rs\n" +run_in_qemu "^C" + +# Script specific checks +{ + grep -q "Success" screenlog.0 +} || { + cat -v screenlog.0 + cat -v /tmp/serial.log + false +} + +rm screenlog.0 \ No newline at end of file