From c9b418799eef6aa5bf740d3a7c5f9c8b9afee7eb Mon Sep 17 00:00:00 2001 From: Yuan Zhuang Date: Tue, 26 Nov 2024 13:24:39 +0000 Subject: [PATCH] projects: add basic eth wallet demo --- projects/README.md | 47 +++++ projects/web3/README.md | 39 ++++ projects/web3/eth_wallet/Makefile | 33 ++++ projects/web3/eth_wallet/host/Cargo.toml | 38 ++++ projects/web3/eth_wallet/host/Makefile | 41 ++++ projects/web3/eth_wallet/host/src/cli.rs | 97 ++++++++++ projects/web3/eth_wallet/host/src/main.rs | 113 +++++++++++ projects/web3/eth_wallet/proto/Cargo.toml | 32 +++ projects/web3/eth_wallet/proto/build.rs | 36 ++++ projects/web3/eth_wallet/proto/src/in_out.rs | 71 +++++++ projects/web3/eth_wallet/proto/src/lib.rs | 43 +++++ projects/web3/eth_wallet/ta/Cargo.toml | 49 +++++ projects/web3/eth_wallet/ta/Makefile | 50 +++++ projects/web3/eth_wallet/ta/Xargo.toml | 24 +++ projects/web3/eth_wallet/ta/build.rs | 108 +++++++++++ projects/web3/eth_wallet/ta/src/hash.rs | 28 +++ projects/web3/eth_wallet/ta/src/main.rs | 182 ++++++++++++++++++ .../web3/eth_wallet/ta/src/secure_storage.rs | 106 ++++++++++ projects/web3/eth_wallet/ta/src/wallet.rs | 142 ++++++++++++++ projects/web3/eth_wallet/ta/ta_static.rs | 102 ++++++++++ projects/web3/eth_wallet/uuid.txt | 1 + 21 files changed, 1382 insertions(+) create mode 100644 projects/README.md create mode 100644 projects/web3/README.md create mode 100644 projects/web3/eth_wallet/Makefile create mode 100644 projects/web3/eth_wallet/host/Cargo.toml create mode 100644 projects/web3/eth_wallet/host/Makefile create mode 100644 projects/web3/eth_wallet/host/src/cli.rs create mode 100644 projects/web3/eth_wallet/host/src/main.rs create mode 100644 projects/web3/eth_wallet/proto/Cargo.toml create mode 100644 projects/web3/eth_wallet/proto/build.rs create mode 100644 projects/web3/eth_wallet/proto/src/in_out.rs create mode 100644 projects/web3/eth_wallet/proto/src/lib.rs create mode 100644 projects/web3/eth_wallet/ta/Cargo.toml create mode 100644 projects/web3/eth_wallet/ta/Makefile create mode 100644 projects/web3/eth_wallet/ta/Xargo.toml create mode 100644 projects/web3/eth_wallet/ta/build.rs create mode 100644 projects/web3/eth_wallet/ta/src/hash.rs create mode 100644 projects/web3/eth_wallet/ta/src/main.rs create mode 100644 projects/web3/eth_wallet/ta/src/secure_storage.rs create mode 100644 projects/web3/eth_wallet/ta/src/wallet.rs create mode 100644 projects/web3/eth_wallet/ta/ta_static.rs create mode 100644 projects/web3/eth_wallet/uuid.txt diff --git a/projects/README.md b/projects/README.md new file mode 100644 index 00000000..658a2343 --- /dev/null +++ b/projects/README.md @@ -0,0 +1,47 @@ +# Projects in Multiple Scenarios + +Trusted Execution Environments (TEEs) play a vital role in providing critical +security solutions across various scenarios. The Teaclave TrustZone SDK +empowers developers to implement robust use cases such as Web3 private key +protection, authentication, and more. + +The `projects/` directory showcases real-world scenarios and essential +primitives designed to help developers build secure applications tailored to +their needs. + +Currently, we have released a Web3-focused scenario, with plans to expand the +project and introduce more use cases in the future. + +## Available Scenarios + +- **Web3**: + Available in `projects/web3/`, this scenario offers utilities for Web3 + development, such as key custodians and decentralized identifiers (DIDs). It + currently includes a basic Ethereum wallet that demonstrates how to securely + create a wallet and sign transactions using wallet-derived keys within the + TEE. + +## Upcoming Scenarios + +- **X509 Certificate Signing & Verification**: + This scenario provides foundational Public Key Infrastructure (PKI) + primitives for securely issuing self-signed certificates and verifying + externally provided leaf certificates using a trusted certificate store. + The Trusted Application (TA) inside the TEE handles secure key pair + generation and certificate issuance, facilitating identity verification for + secure communications. This primitive is particularly valuable for + establishing trusted communication channels between nodes or devices. + +- **Remote Attestation**: + This foundational primitive enables remote attestation of a Trusted + Application (TA) to ensure it is running within a Trusted Execution + Environment (TEE). It utilizes TLS and X509 PKI to establish a secure + communication channel. + +- **Multi-Factor Authentication (MFA)**: + This example demonstrates how to implement MFA by securely provisioning the + public keys of trusted MFA devices (e.g., a user’s cellphone) within the + Trusted Application (TA). When high-risk operations like key usage or + transaction signing require user confirmation, the TA securely verifies + user-provided details via the trusted MFA device, eliminating reliance on + third-party services. diff --git a/projects/web3/README.md b/projects/web3/README.md new file mode 100644 index 00000000..5d5a0632 --- /dev/null +++ b/projects/web3/README.md @@ -0,0 +1,39 @@ +# Reference Implementation Examples for Web3 Trusted Applications + +Teaclave TrustZone SDK allows developers to create Trusted Applications +(TAs) in Rust, offering a memory-safe and secure environment. Many examples +in this repository are ported from OP-TEE C examples. With Rust's ecosystem +and support for Rust-std in Teaclave TrustZone SDK, developers can build +secure TAs to protect confidential information. + +In Web3, private key protection is vital for securing on-chain identities and +assets. TAs safeguard the entire lifecycle of Web3 credentials used in wallets +or validator key protection. In DePIN, TAs enable secure device attestation, +helping to prevent Sybil attacks. + +This directory contains a collection of reference implementations of TAs, +specifically tailored for Web3 use cases. These examples demonstrate how +to use Rust within TrustZone to support basic Web3 use cases. We will +gradually open-source each of them as reference implementation examples +for Web3 TAs. Web3 builders can leverage these examples to integrate secure +functionalities into their projects, particularly in environments where OP-TEE +and TrustZone technologies are employed. + +## Basic Web3 Wallet +**AVAILABLE** in [eth-wallet/](./eth-wallet) + +A wallet abstraction featuring key functionalities like secure key management +and transaction signing. The key management includes secure seed generation, +mnemonic derivation, and safe key storage within external TEE-protected +environments. For transaction signing, we demonstrate how to securely sign +an Ethereum transaction using wallet-derived keys inside the TEE, ensuring +the private keys never leave the trusted environment. + +## Decentralized Identifier (DID) +**To Be Released** + +This example will illustrate how to integrate Decentralized Identifiers (DIDs) +into TAs. DIDs enable self-sovereign identity by proving ownership without +relying on central authorities. Secure key management for creating and +operating DIDs ensures reliable device identification, mitigating the risk of +fake devices in DePIN. diff --git a/projects/web3/eth_wallet/Makefile b/projects/web3/eth_wallet/Makefile new file mode 100644 index 00000000..c266055b --- /dev/null +++ b/projects/web3/eth_wallet/Makefile @@ -0,0 +1,33 @@ +# 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 + +all: + $(q)make -C host TARGET_HOST=$(TARGET_HOST) \ + CROSS_COMPILE_HOST=$(CROSS_COMPILE_HOST) + $(q)make -C ta TARGET_TA=$(TARGET_TA) \ + CROSS_COMPILE_TA=$(CROSS_COMPILE_TA) + +clean: + $(q)make -C host clean + $(q)make -C ta clean diff --git a/projects/web3/eth_wallet/host/Cargo.toml b/projects/web3/eth_wallet/host/Cargo.toml new file mode 100644 index 00000000..e1e36bf7 --- /dev/null +++ b/projects/web3/eth_wallet/host/Cargo.toml @@ -0,0 +1,38 @@ +# 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 = "eth_wallet-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" } + +structopt = "0.3" +bincode = "1.3.3" +anyhow = "1.0" +uuid = { version = "1.8", features = ["serde"] } +hex = { version = "0.4", features = ["serde"] } + +[profile.release] +lto = true diff --git a/projects/web3/eth_wallet/host/Makefile b/projects/web3/eth_wallet/host/Makefile new file mode 100644 index 00000000..b1c50c41 --- /dev/null +++ b/projects/web3/eth_wallet/host/Makefile @@ -0,0 +1,41 @@ +# 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. + +NAME := eth_wallet-rs + +TARGET_HOST ?= aarch64-unknown-linux-gnu +CROSS_COMPILE_HOST ?= aarch64-linux-gnu- +OBJCOPY := $(CROSS_COMPILE_HOST)objcopy +LINKER_CFG := target.$(TARGET_HOST).linker=\"$(CROSS_COMPILE_HOST)gcc\" + +OUT_DIR := $(CURDIR)/target/$(TARGET_HOST)/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/projects/web3/eth_wallet/host/src/cli.rs b/projects/web3/eth_wallet/host/src/cli.rs new file mode 100644 index 00000000..8d905462 --- /dev/null +++ b/projects/web3/eth_wallet/host/src/cli.rs @@ -0,0 +1,97 @@ +// 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::{bail, Result}; +use structopt::StructOpt; + +// decode hex string to [u8; 20] +pub fn decode_hex_to_address(src: &str) -> Result<[u8; 20]> { + // strip the 0x prefix + let src = src.trim_start_matches("0x"); + let vec = hex::decode(src)?; + if vec.len() < 20 { + bail!("invalid address length: {}", vec.len()); + } + let mut array = [0u8; 20]; + array.copy_from_slice(&vec[..20]); + Ok(array) +} + +// decode string to uuid +pub fn decode_str_to_uuid(s: &str) -> Result { + uuid::Uuid::parse_str(s).map_err(|e| e.into()) +} + +#[derive(Debug, StructOpt)] +pub struct CreateWalletOpt {} + +#[derive(Debug, StructOpt)] +pub struct RemoveWalletOpt { + #[structopt(short, long, required = true)] + pub wallet_id: uuid::Uuid, +} + +#[derive(Debug, StructOpt)] +pub struct DeriveAddressOpt { + #[structopt(short, long, required = true)] + pub wallet_id: uuid::Uuid, + #[structopt(short, long, required = true, default_value = "m/44'/60'/0'/0/0")] + pub hd_path: String, +} + +#[derive(Debug, StructOpt)] +pub struct SignTransactionOpt { + #[structopt(short, long, required = true, parse(try_from_str = decode_str_to_uuid))] + pub wallet_id: uuid::Uuid, + #[structopt(short, long, default_value = "m/44'/60'/0'/0/0")] + pub hd_path: String, + #[structopt(short, long, default_value = "5")] + pub chain_id: u64, + #[structopt(short, long, default_value = "0")] + pub nonce: u128, + #[structopt(short, long, required = true, parse(try_from_str = decode_hex_to_address))] + pub to: [u8; 20], + #[structopt(short, long, required = true)] + pub value: u128, + #[structopt(short = "p", long, default_value = "1000000000")] + pub gas_price: u128, + #[structopt(short, long, default_value = "21000")] + pub gas: u128, +} + +#[derive(Debug, StructOpt)] +pub enum Command { + /// Create a new wallet. + #[structopt(name = "create-wallet")] + CreateWallet(CreateWalletOpt), + /// Remove a wallet. + #[structopt(name = "remove-wallet")] + RemoveWallet(RemoveWalletOpt), + /// Derive an address from a wallet. + #[structopt(name = "derive-address")] + DeriveAddress(DeriveAddressOpt), + /// Sign a transaction. + #[structopt(name = "sign-transaction")] + SignTransaction(SignTransactionOpt), +} + +#[derive(Debug, StructOpt)] +#[structopt(name = "eth_wallet", about = "A simple Ethereum wallet based on TEE")] +pub struct Opt { + #[structopt(subcommand)] + pub command: Command, +} diff --git a/projects/web3/eth_wallet/host/src/main.rs b/projects/web3/eth_wallet/host/src/main.rs new file mode 100644 index 00000000..44089955 --- /dev/null +++ b/projects/web3/eth_wallet/host/src/main.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. + +mod cli; + +use optee_teec::{Context, Operation, ParamType, Uuid}; +use optee_teec::{ParamNone, ParamTmpRef, ParamValue}; + +use anyhow::{bail, Result}; +use structopt::StructOpt; + +const OUTPUT_MAX_SIZE: usize = 1024; + +fn invoke_command(command: proto::Command, input: &[u8]) -> optee_teec::Result> { + let mut ctx = Context::new()?; + let uuid = Uuid::parse_str(proto::UUID) + .map_err(|_| optee_teec::Error::new(optee_teec::ErrorKind::ItemNotFound))?; + let mut session = ctx.open_session(uuid)?; + + println!("CA: command: {:?}", command); + // input buffer + let p0 = ParamTmpRef::new_input(input); + // output buffer + let mut output = vec![0u8; OUTPUT_MAX_SIZE]; + let p1 = ParamTmpRef::new_output(output.as_mut_slice()); + // output buffer size + let p2 = ParamValue::new(0, 0, ParamType::ValueInout); + + let mut operation = Operation::new(0, p0, p1, p2, ParamNone); + match session.invoke_command(command as u32, &mut operation) { + Ok(()) => { + println!("CA: invoke_command success"); + let output_len = operation.parameters().2.a() as usize; + Ok(output[..output_len].to_vec()) + } + Err(e) => { + let output_len = operation.parameters().2.a() as usize; + let err_message = String::from_utf8_lossy(&output[..output_len]); + println!("CA: invoke_command failed: {:?}", err_message); + Err(e) + } + } +} + +fn main() -> Result<()> { + let args = cli::Opt::from_args(); + match args.command { + cli::Command::CreateWallet(_opt) => { + let serialized_output = invoke_command(proto::Command::CreateWallet, &[])?; + let output: proto::CreateWalletOutput = bincode::deserialize(&serialized_output)?; + println!("Wallet ID: {}", output.wallet_id); + } + cli::Command::RemoveWallet(opt) => { + let input = proto::RemoveWalletInput { + wallet_id: opt.wallet_id, + }; + let _output = + invoke_command(proto::Command::RemoveWallet, &bincode::serialize(&input)?)?; + println!("Wallet removed"); + } + cli::Command::DeriveAddress(opt) => { + let input = proto::DeriveAddressInput { + wallet_id: opt.wallet_id, + hd_path: opt.hd_path, + }; + let serialized_output = + invoke_command(proto::Command::DeriveAddress, &bincode::serialize(&input)?)?; + let output: proto::DeriveAddressOutput = bincode::deserialize(&serialized_output)?; + println!("Address: 0x{}", hex::encode(&output.address)); + println!("Public key: {}", hex::encode(&output.public_key)); + } + cli::Command::SignTransaction(opt) => { + let transaction = proto::EthTransaction { + chain_id: opt.chain_id, + nonce: opt.nonce, + to: Some(opt.to), + value: opt.value, + gas_price: opt.gas_price, + gas: opt.gas, + data: vec![], + }; + let input = proto::SignTransactionInput { + wallet_id: opt.wallet_id, + hd_path: opt.hd_path, + transaction, + }; + let serialized_output = invoke_command( + proto::Command::SignTransaction, + &bincode::serialize(&input)?, + )?; + let output: proto::SignTransactionOutput = bincode::deserialize(&serialized_output)?; + println!("Signature: {}", hex::encode(&output.signature)); + } + _ => { + bail!("Unsupported command"); + } + } + Ok(()) +} diff --git a/projects/web3/eth_wallet/proto/Cargo.toml b/projects/web3/eth_wallet/proto/Cargo.toml new file mode 100644 index 00000000..1bd36816 --- /dev/null +++ b/projects/web3/eth_wallet/proto/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 = "proto" +version = "0.3.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] +uuid = { version = "1.8", features = ["serde"] } +serde = { version = "1.0", features = ["derive"] } + +[build-dependencies] +uuid = { version = "1.8", default-features = false } diff --git a/projects/web3/eth_wallet/proto/build.rs b/projects/web3/eth_wallet/proto/build.rs new file mode 100644 index 00000000..b9d06122 --- /dev/null +++ b/projects/web3/eth_wallet/proto/build.rs @@ -0,0 +1,36 @@ +// 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::fs; +use std::path::PathBuf; +use std::fs::File; +use std::env; +use std::io::Write; + +fn main() { + let uuid = match fs::read_to_string("../uuid.txt") { + Ok(u) => { + u.trim().to_string() + }, + Err(_) => { + panic!("Cannot find uuid.txt"); + } + }; + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let mut buffer = File::create(out.join("uuid.txt")).unwrap(); + write!(buffer, "{}", uuid).unwrap(); +} diff --git a/projects/web3/eth_wallet/proto/src/in_out.rs b/projects/web3/eth_wallet/proto/src/in_out.rs new file mode 100644 index 00000000..36a5bcee --- /dev/null +++ b/projects/web3/eth_wallet/proto/src/in_out.rs @@ -0,0 +1,71 @@ +// 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 serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CreateWalletInput {} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CreateWalletOutput { + pub wallet_id: Uuid, + pub mnemonic: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RemoveWalletInput { + pub wallet_id: Uuid, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RemoveWalletOutput {} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DeriveAddressInput { + pub wallet_id: Uuid, + pub hd_path: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DeriveAddressOutput { + pub address: [u8; 20], + pub public_key: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EthTransaction { + pub chain_id: u64, + pub nonce: u128, + pub to: Option<[u8; 20]>, + pub value: u128, + pub gas_price: u128, + pub gas: u128, + pub data: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SignTransactionInput { + pub wallet_id: Uuid, + pub hd_path: String, + pub transaction: EthTransaction, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SignTransactionOutput { + pub signature: Vec, +} diff --git a/projects/web3/eth_wallet/proto/src/lib.rs b/projects/web3/eth_wallet/proto/src/lib.rs new file mode 100644 index 00000000..87c42bb3 --- /dev/null +++ b/projects/web3/eth_wallet/proto/src/lib.rs @@ -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. + +mod in_out; +pub use in_out::*; + +#[derive(Debug)] +pub enum Command { + CreateWallet, + RemoveWallet, + DeriveAddress, + SignTransaction, + Unknown, +} + +impl From for Command { + #[inline] + fn from(value: u32) -> Command { + match value { + 0 => Command::CreateWallet, + 1 => Command::RemoveWallet, + 2 => Command::DeriveAddress, + 3 => Command::SignTransaction, + _ => Command::Unknown, + } + } +} + +pub const UUID: &str = &include_str!(concat!(env!("OUT_DIR"), "/uuid.txt")); diff --git a/projects/web3/eth_wallet/ta/Cargo.toml b/projects/web3/eth_wallet/ta/Cargo.toml new file mode 100644 index 00000000..e118e599 --- /dev/null +++ b/projects/web3/eth_wallet/ta/Cargo.toml @@ -0,0 +1,49 @@ +# 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.3.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] +libc = { path = "../../../../rust/libc" } +proto = { path = "../proto" } +optee-utee-sys = { path = "../../../../optee-utee/optee-utee-sys" } +optee-utee = { path = "../../../../optee-utee" } + +anyhow = "1.0" +uuid = { version = "1.8", default-features = false } +bip32 = { version = "0.3.0", features = ["bip39"]} +hex = { version = "0.4", features = ["serde"] } +serde = { version = "1.0", features = ["derive"] } +sha3 = "0.10.6" +secp256k1 = "0.27.0" +ethereum-tx-sign = "6.1.3" +bincode = "1.3.3" + +[build-dependencies] +uuid = { version = "1.8", default-features = false } +proto = { path = "../proto" } + +[profile.release] +lto = false +opt-level = 3 diff --git a/projects/web3/eth_wallet/ta/Makefile b/projects/web3/eth_wallet/ta/Makefile new file mode 100644 index 00000000..46037a8e --- /dev/null +++ b/projects/web3/eth_wallet/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_TA ?= aarch64-unknown-linux-gnu +CROSS_COMPILE_TA ?= aarch64-linux-gnu- +OBJCOPY := $(CROSS_COMPILE_TA)objcopy +LINKER_CFG := target.$(TARGET_TA).linker=\"$(CROSS_COMPILE_TA)ld.bfd\" + +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_TA)/release + +ifeq ($(STD),) +all: + @echo "Please `export STD=y` then rerun `source environment` to build the STD version" +else +all: ta strip sign +endif + +# set the cross compile for building inner libraries, such as C libraries in ring +ta: + @CROSS_COMPILE=$(CROSS_COMPILE_TA) xargo build --target $(TARGET_TA) --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 \ No newline at end of file diff --git a/projects/web3/eth_wallet/ta/Xargo.toml b/projects/web3/eth_wallet/ta/Xargo.toml new file mode 100644 index 00000000..251ee70e --- /dev/null +++ b/projects/web3/eth_wallet/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/projects/web3/eth_wallet/ta/build.rs b/projects/web3/eth_wallet/ta/build.rs new file mode 100644 index 00000000..9efe628c --- /dev/null +++ b/projects/web3/eth_wallet/ta/build.rs @@ -0,0 +1,108 @@ +// 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 proto; +use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader, Write}; +use std::path::{Path, PathBuf}; +use uuid::Uuid; + +fn main() -> std::io::Result<()> { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + let mut buffer = File::create(out.join("user_ta_header.rs"))?; + buffer.write_all(include_bytes!("ta_static.rs"))?; + + let tee_uuid = Uuid::parse_str(proto::UUID).unwrap(); + let (time_low, time_mid, time_hi_and_version, clock_seq_and_node) = tee_uuid.as_fields(); + + write!(buffer, "\n")?; + write!( + buffer, + "const TA_UUID: optee_utee_sys::TEE_UUID = optee_utee_sys::TEE_UUID {{ + timeLow: {:#x}, + timeMid: {:#x}, + timeHiAndVersion: {:#x}, + clockSeqAndNode: {:#x?}, +}};", + time_low, time_mid, time_hi_and_version, clock_seq_and_node + )?; + + let mut aarch64_flag = true; + match env::var("TARGET_TA") { + Ok(ref v) if v == "arm-unknown-linux-gnueabihf" || v == "arm-unknown-optee" => { + println!("cargo:rustc-link-arg=--no-warn-mismatch"); + aarch64_flag = false; + } + _ => {} + }; + + let optee_os_dir = env::var("TA_DEV_KIT_DIR").unwrap(); + let search_path = Path::new(&optee_os_dir).join("lib"); + + let optee_os_path = &PathBuf::from(optee_os_dir.clone()); + let mut ta_lds = File::create(out.join("ta.lds"))?; + let f = File::open(optee_os_path.join("src/ta.ld.S"))?; + let f = BufReader::new(f); + + for line in f.lines() { + let l = line?; + + if aarch64_flag { + if l.starts_with('#') + || l == "OUTPUT_FORMAT(\"elf32-littlearm\")" + || l == "OUTPUT_ARCH(arm)" + { + continue; + } + } else { + if l.starts_with('#') + || l == "OUTPUT_FORMAT(\"elf64-littleaarch64\")" + || l == "OUTPUT_ARCH(aarch64)" + { + continue; + } + } + + if l == "\t. = ALIGN(4096);" { + write!(ta_lds, "\t. = ALIGN(65536);\n")?; + } else { + write!(ta_lds, "{}\n", l)?; + } + } + + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=ta.lds"); + + println!("cargo:rustc-link-search={}", search_path.display()); + println!("cargo:rustc-link-lib=static=utee"); + println!("cargo:rustc-link-lib=static=utils"); + println!("cargo:rustc-link-arg=-Tta.lds"); + println!("cargo:rustc-link-arg=-e__ta_entry"); + println!("cargo:rustc-link-arg=-pie"); + println!("cargo:rustc-link-arg=-Os"); + println!("cargo:rustc-link-arg=--sort-section=alignment"); + + let mut dyn_list = File::create(out.join("dyn_list"))?; + write!( + dyn_list, + "{{ __elf_phdr_info; trace_ext_prefix; trace_level; ta_head; }};\n" + )?; + println!("cargo:rustc-link-arg=--dynamic-list=dyn_list"); + Ok(()) +} diff --git a/projects/web3/eth_wallet/ta/src/hash.rs b/projects/web3/eth_wallet/ta/src/hash.rs new file mode 100644 index 00000000..9b5f3a60 --- /dev/null +++ b/projects/web3/eth_wallet/ta/src/hash.rs @@ -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. + +use serde::Serialize; +use sha3::{Digest, Keccak256}; + +pub fn keccak_hash_to_bytes(data: &T) -> Vec +where + T: ?Sized + Serialize + AsRef<[u8]>, +{ + let mut hasher = Keccak256::new(); + hasher.update(data); + hasher.finalize().to_vec() +} diff --git a/projects/web3/eth_wallet/ta/src/main.rs b/projects/web3/eth_wallet/ta/src/main.rs new file mode 100644 index 00000000..31461863 --- /dev/null +++ b/projects/web3/eth_wallet/ta/src/main.rs @@ -0,0 +1,182 @@ +// 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] +#![feature(c_size_t)] + +mod hash; +mod secure_storage; +mod wallet; + +use crate::secure_storage::{ + delete_from_secure_storage, load_from_secure_storage, save_in_secure_storage, +}; +use optee_utee::{ + ta_close_session, ta_create, ta_destroy, ta_invoke_command, ta_open_session, trace_println, +}; +use optee_utee::{Error, ErrorKind, Parameters}; +use proto::Command; + +use anyhow::{anyhow, bail, Result}; +use std::convert::TryInto; +use std::io::Write; +use wallet::Wallet; + +#[ta_create] +fn create() -> optee_utee::Result<()> { + trace_println!("[+] TA create"); + Ok(()) +} + +#[ta_open_session] +fn open_session(_params: &mut Parameters) -> optee_utee::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"); +} + +#[cfg(debug_assertions)] +macro_rules! dbg_println { + ($($arg:tt)*) => (trace_println!($($arg)*)); +} + +#[cfg(not(debug_assertions))] +macro_rules! dbg_println { + ($($arg:tt)*) => {}; +} + +fn create_wallet(_input: &proto::CreateWalletInput) -> Result { + let wallet = Wallet::new()?; + let wallet_id = wallet.get_id(); + let mnemonic = wallet.get_mnemonic()?; + dbg_println!("[+] Wallet ID: {:?}", wallet_id); + + let secure_object: Vec = wallet.try_into()?; + save_in_secure_storage(wallet_id.as_bytes(), &secure_object)?; + dbg_println!("[+] Wallet saved in secure storage"); + + Ok(proto::CreateWalletOutput { + wallet_id, + mnemonic, + }) +} + +fn remove_wallet(input: &proto::RemoveWalletInput) -> Result { + dbg_println!("[+] Removing wallet: {:?}", input.wallet_id); + + delete_from_secure_storage(input.wallet_id.as_bytes())?; + dbg_println!("[+] Wallet removed"); + + Ok(proto::RemoveWalletOutput {}) +} + +fn derive_address(input: &proto::DeriveAddressInput) -> Result { + let secure_object = load_from_secure_storage(input.wallet_id.as_bytes()) + .map_err(|e| anyhow!("[+] Deriving address: error: wallet not found: {:?}", e))?; + dbg_println!("[+] Deriving address: secure object loaded"); + + let wallet: Wallet = secure_object.try_into()?; + let (address, public_key) = wallet.derive_address(&input.hd_path)?; + dbg_println!("[+] Deriving address: address: {:?}", address); + dbg_println!("[+] Deriving address: public key: {:?}", public_key); + + Ok(proto::DeriveAddressOutput { + address, + public_key, + }) +} + +fn sign_transaction(input: &proto::SignTransactionInput) -> Result { + let secure_object = load_from_secure_storage(input.wallet_id.as_bytes()) + .map_err(|e| anyhow!("[+] Sign transaction: error: wallet not found: {:?}", e))?; + dbg_println!("[+] Sign transaction: secure object loaded"); + + let wallet: Wallet = secure_object.try_into()?; + let signature = wallet.sign_transaction(&input.hd_path, &input.transaction)?; + dbg_println!("[+] Sign transaction: signature: {:?}", signature); + + Ok(proto::SignTransactionOutput { signature }) +} + +fn handle_invoke(command: Command, serialized_input: &[u8]) -> Result> { + fn process Result>( + serialized_input: &[u8], + handler: F, + ) -> Result> { + let input: T = bincode::deserialize(serialized_input)?; + let output = handler(&input)?; + let serialized_output = bincode::serialize(&output)?; + Ok(serialized_output) + } + + match command { + Command::CreateWallet => process(serialized_input, create_wallet), + Command::RemoveWallet => process(serialized_input, remove_wallet), + Command::DeriveAddress => process(serialized_input, derive_address), + Command::SignTransaction => process(serialized_input, sign_transaction), + _ => bail!("Unsupported command"), + } +} + +#[ta_invoke_command] +fn invoke_command(cmd_id: u32, params: &mut Parameters) -> optee_utee::Result<()> { + dbg_println!("[+] TA invoke command"); + let mut p0 = unsafe { params.0.as_memref().unwrap() }; + let mut p1 = unsafe { params.1.as_memref().unwrap() }; + let mut p2 = unsafe { params.2.as_value().unwrap() }; + + let output_vec = match handle_invoke(Command::from(cmd_id), p0.buffer()) { + Ok(output) => output, + Err(e) => { + let err_message = format!("{:?}", e).as_bytes().to_vec(); + p1.buffer() + .write(&err_message) + .map_err(|_| Error::new(ErrorKind::BadState))?; + p2.set_a(err_message.len() as u32); + return Err(Error::new(ErrorKind::BadParameters)); + } + }; + p1.buffer() + .write(&output_vec) + .map_err(|_| Error::new(ErrorKind::BadState))?; + p2.set_a(output_vec.len() as u32); + + Ok(()) +} + +// TA configurations +const TA_FLAGS: u32 = 0; +const TA_DATA_SIZE: u32 = 1024 * 1024; +const TA_STACK_SIZE: u32 = 128 * 1024; +const TA_VERSION: &[u8] = b"0.3\0"; +const TA_DESCRIPTION: &[u8] = b"This is an example of Ethereum wallet TA\0"; +const EXT_PROP_VALUE_1: &[u8] = b"Ethereum wallet TA\0"; +const EXT_PROP_VALUE_2: u32 = 0x0010; +const TRACE_LEVEL: i32 = 4; +const TRACE_EXT_PREFIX: &[u8] = b"TA\0"; +const TA_FRAMEWORK_STACK_SIZE: u32 = 2048; + +include!(concat!(env!("OUT_DIR"), "/user_ta_header.rs")); diff --git a/projects/web3/eth_wallet/ta/src/secure_storage.rs b/projects/web3/eth_wallet/ta/src/secure_storage.rs new file mode 100644 index 00000000..808adb78 --- /dev/null +++ b/projects/web3/eth_wallet/ta/src/secure_storage.rs @@ -0,0 +1,106 @@ +// 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::{bail, Result}; +use optee_utee::{DataFlag, ObjectStorageConstants, PersistentObject}; + +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; + + let mut init_data: [u8; 0] = [0; 0]; + match PersistentObject::create( + ObjectStorageConstants::Private, + obj_id, + obj_data_flag, + None, + &mut init_data, + ) { + Err(e) => { + bail!("[-] {:?}: failed to create object: {:?}", &obj_id, e); + } + + Ok(mut object) => match object.write(&data) { + Ok(()) => { + return Ok(()); + } + Err(e_write) => { + object.close_and_delete()?; + std::mem::forget(object); + bail!( + "[-] {:?}: failed to write data to object: {:?}", + &obj_id, + e_write + ); + } + }, + } +} + +pub fn load_from_secure_storage(obj_id: &[u8]) -> Result> { + let mut buf = vec![0; 5000]; + + match PersistentObject::open( + ObjectStorageConstants::Private, + obj_id, + DataFlag::ACCESS_READ | DataFlag::SHARE_READ, + ) { + Err(e) => bail!("[-] {:?}: failed to open object: {:?}", &obj_id, e), + + Ok(object) => { + let obj_info = object.info()?; + + if obj_info.data_size() > buf.len() { + bail!("[-] {:?}: data size is too large", &obj_id); + } + let read_bytes = match object.read(&mut buf) { + Ok(read_bytes) => read_bytes, + Err(e) => { + bail!("[-] {:?}: failed to read data: {:?}", &obj_id, e); + } + }; + + if read_bytes != obj_info.data_size() as u32 { + bail!("[-] {:?}: failed to read data", &obj_id); + } + + buf.truncate(read_bytes as usize); + } + } + + Ok(buf) +} + +pub fn delete_from_secure_storage(obj_id: &[u8]) -> Result<()> { + match PersistentObject::open( + ObjectStorageConstants::Private, + &mut obj_id.to_vec(), + 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/projects/web3/eth_wallet/ta/src/wallet.rs b/projects/web3/eth_wallet/ta/src/wallet.rs new file mode 100644 index 00000000..4072061d --- /dev/null +++ b/projects/web3/eth_wallet/ta/src/wallet.rs @@ -0,0 +1,142 @@ +// 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, Result}; +use bip32::{Mnemonic, XPrv}; +use serde::{Deserialize, Serialize}; +use std::convert::{TryFrom, TryInto}; +use uuid::Uuid; + +use crate::hash::keccak_hash_to_bytes; +use ethereum_tx_sign::Transaction; +use optee_utee::Random; +use proto::EthTransaction; + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct Wallet { + id: Uuid, + entropy: Vec, +} + +impl Wallet { + pub fn new() -> Result { + let mut entropy = vec![0u8; 32]; + Random::generate(entropy.as_mut() as _); + + let mut random_bytes = vec![0u8; 16]; + Random::generate(random_bytes.as_mut() as _); + let uuid = uuid::Builder::from_random_bytes( + random_bytes + .try_into() + .map_err(|_| anyhow!("[-] Wallet::new(): invalid random bytes"))?, + ) + .into_uuid(); + + Ok(Self { + id: uuid, + entropy: entropy, + }) + } + + pub fn get_id(&self) -> Uuid { + self.id.clone() + } + + pub fn get_mnemonic(&self) -> Result { + let mnemonic = Mnemonic::from_entropy( + self.entropy.as_slice().try_into()?, + bip32::Language::English, + ); + Ok(mnemonic.phrase().to_string()) + } + + pub fn get_seed(&self) -> Result> { + let mnemonic = Mnemonic::from_entropy( + self.entropy.as_slice().try_into()?, + bip32::Language::English, + ); + let seed = mnemonic.to_seed(""); // empty passwords + Ok(seed.as_bytes().to_vec()) + } + + pub fn derive_prv_key(&self, hd_path: &str) -> Result> { + let path = hd_path.parse()?; + let child_xprv = XPrv::derive_from_path(self.get_seed()?, &path)?; + let child_xprv_bytes = child_xprv.to_bytes(); + Ok(child_xprv_bytes.to_vec()) + } + + pub fn derive_pub_key(&self, hd_path: &str) -> Result> { + let path = hd_path.parse()?; + let child_xprv = XPrv::derive_from_path(self.get_seed()?, &path)?; + // public key + let child_xpub_bytes = child_xprv.public_key().to_bytes(); + Ok(child_xpub_bytes.to_vec()) + } + + pub fn derive_address(&self, hd_path: &str) -> Result<([u8; 20], Vec)> { + let public_key_bytes = self.derive_pub_key(hd_path)?; + // uncompress public key + let public_key = secp256k1::PublicKey::from_slice(&public_key_bytes)?; + let uncompressed_public_key = &public_key.serialize_uncompressed()[1..]; + + // pubkey to address + let address = &keccak_hash_to_bytes(&uncompressed_public_key)[12..]; + Ok((address.try_into()?, public_key_bytes)) + } + + pub fn sign_transaction(&self, hd_path: &str, transaction: &EthTransaction) -> Result> { + let xprv = self.derive_prv_key(hd_path)?; + let legacy_transaction = ethereum_tx_sign::LegacyTransaction { + chain: transaction.chain_id, + nonce: transaction.nonce, + gas_price: transaction.gas_price, + gas: transaction.gas, + to: transaction.to, + value: transaction.value, + data: transaction.data.clone(), + }; + let ecdsa = legacy_transaction.ecdsa(&xprv).map_err(|e| { + let ethereum_tx_sign::Error::Secp256k1(inner_error) = e; + inner_error + })?; + let signature = legacy_transaction.sign(&ecdsa); + Ok(signature) + } +} + +impl TryFrom for Vec { + type Error = anyhow::Error; + + fn try_from(wallet: Wallet) -> Result> { + bincode::serialize(&wallet).map_err(|e| anyhow!("[-] Wallet::try_into(): {:?}", e)) + } +} + +impl TryFrom> for Wallet { + type Error = anyhow::Error; + + fn try_from(data: Vec) -> Result { + bincode::deserialize(&data).map_err(|e| anyhow!("[-] Wallet::try_from(): {:?}", e)) + } +} + +impl Drop for Wallet { + fn drop(&mut self) { + self.entropy.iter_mut().for_each(|x| *x = 0); + } +} diff --git a/projects/web3/eth_wallet/ta/ta_static.rs b/projects/web3/eth_wallet/ta/ta_static.rs new file mode 100644 index 00000000..53ca2109 --- /dev/null +++ b/projects/web3/eth_wallet/ta/ta_static.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 core::ffi::*; +use core::mem; +use core::primitive::u64; + +#[no_mangle] +pub static mut trace_level: c_int = TRACE_LEVEL; + +#[no_mangle] +pub static trace_ext_prefix: &[u8] = TRACE_EXT_PREFIX; + +#[no_mangle] +#[link_section = ".ta_head"] +pub static ta_head: optee_utee_sys::ta_head = optee_utee_sys::ta_head { + uuid: TA_UUID, + stack_size: TA_STACK_SIZE + TA_FRAMEWORK_STACK_SIZE, + flags: TA_FLAGS, + depr_entry: u64::MAX, +}; + +#[no_mangle] +#[link_section = ".bss"] +pub static ta_heap: [u8; TA_DATA_SIZE as usize] = [0; TA_DATA_SIZE as usize]; + +#[no_mangle] +pub static ta_heap_size: c_size_t = mem::size_of::() * TA_DATA_SIZE as usize; +static FLAG_BOOL: bool = (TA_FLAGS & optee_utee_sys::TA_FLAG_SINGLE_INSTANCE) != 0; +static FLAG_MULTI: bool = (TA_FLAGS & optee_utee_sys::TA_FLAG_MULTI_SESSION) != 0; +static FLAG_INSTANCE: bool = (TA_FLAGS & optee_utee_sys::TA_FLAG_INSTANCE_KEEP_ALIVE) != 0; + +#[no_mangle] +pub static ta_num_props: c_size_t = 9; + +#[no_mangle] +pub static ta_props: [optee_utee_sys::user_ta_property; 9] = [ + optee_utee_sys::user_ta_property { + name: optee_utee_sys::TA_PROP_STR_SINGLE_INSTANCE, + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BOOL, + value: &FLAG_BOOL as *const bool as *mut _, + }, + optee_utee_sys::user_ta_property { + name: optee_utee_sys::TA_PROP_STR_MULTI_SESSION, + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BOOL, + value: &FLAG_MULTI as *const bool as *mut _, + }, + optee_utee_sys::user_ta_property { + name: optee_utee_sys::TA_PROP_STR_KEEP_ALIVE, + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BOOL, + value: &FLAG_INSTANCE as *const bool as *mut _, + }, + optee_utee_sys::user_ta_property { + name: optee_utee_sys::TA_PROP_STR_DATA_SIZE, + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_U32, + value: &TA_DATA_SIZE as *const u32 as *mut _, + }, + optee_utee_sys::user_ta_property { + name: optee_utee_sys::TA_PROP_STR_STACK_SIZE, + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_U32, + value: &TA_STACK_SIZE as *const u32 as *mut _, + }, + optee_utee_sys::user_ta_property { + name: optee_utee_sys::TA_PROP_STR_VERSION, + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_STRING, + value: TA_VERSION as *const [u8] as *mut _, + }, + optee_utee_sys::user_ta_property { + name: optee_utee_sys::TA_PROP_STR_DESCRIPTION, + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_STRING, + value: TA_DESCRIPTION as *const [u8] as *mut _, + }, + optee_utee_sys::user_ta_property { + name: "gp.ta.description\0".as_ptr(), + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_STRING, + value: EXT_PROP_VALUE_1 as *const [u8] as *mut _, + }, + optee_utee_sys::user_ta_property { + name: "gp.ta.version\0".as_ptr(), + prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_U32, + value: &EXT_PROP_VALUE_2 as *const u32 as *mut _, + }, +]; + +#[no_mangle] +pub unsafe extern "C" fn tahead_get_trace_level() -> c_int { + return trace_level; +} diff --git a/projects/web3/eth_wallet/uuid.txt b/projects/web3/eth_wallet/uuid.txt new file mode 100644 index 00000000..c0c0f3f4 --- /dev/null +++ b/projects/web3/eth_wallet/uuid.txt @@ -0,0 +1 @@ +be2dc9a0-02b4-4b33-ba21-9964dbdf1573