Skip to content
This repository has been archived by the owner on Jun 21, 2020. It is now read-only.

Commit

Permalink
DOC: Documented engima-types
Browse files Browse the repository at this point in the history
  • Loading branch information
elichai committed May 19, 2019
1 parent 2d4e18f commit 19319de
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 3 deletions.
4 changes: 3 additions & 1 deletion enigma-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# I use package renaming to import 2 libraries with the same name but from different sources (1 for SGX and 1 for regular std)
# Then in the code you can rename them back (under a cfg condition) to the same name to use abstractly.

[package]
name = "enigma-types"
Expand All @@ -16,7 +18,7 @@ serde_std = { package = "serde", version = "1.0", default-features = false }
cbindgen = "0.8"

[features]
default = ["std"]
default = ["std"] # This library should work without std if `--no-default-features` is supplied.
std = ["serde_std/std"]
alloc = ["serde_std/alloc"]
sgx = ["serde_sgx"]
34 changes: 33 additions & 1 deletion enigma-types/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
//! # Hash Module
//! This module provides a struct meant for containing Hashes (Kecack256 or Sha256).
use core::ops::{Deref, DerefMut};
use rustc_hex::{FromHex, FromHexError};
use arrayvec::ArrayVec;

/// This struct is basically a wrapper over `[u8; 32]`, and is meant to be returned from whatever hashing functions we use.
/// `#[repr(C)]` is a Rust feature which makes the struct be aligned just like C structs.
/// See [`Repr(C)`][https://doc.rust-lang.org/nomicon/other-reprs.html]
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
pub struct Hash256([u8; 32]);


impl Hash256 {
pub fn copy_from_slice(&mut self, src: &[u8]) {

/// This method exposes rust's built in [`std::slice::copy_from_slice`]
/// Copies the elements from `src` into `self`.
///
/// The length of `src` must be the same as `self`.
///
/// # Panics
///
/// This function will panic if the two slices have different lengths.
///
/// This might not be needed since we implement `Deref` and `DerefMut` for the inner array.
pub fn copy_from_slice(&mut self, src: &[u8]) {
self.0.copy_from_slice(src)
}

/// This function converts a hex string into `Hash256` type.
/// the hex must not contain `0x` (as is usually the case in hexs in rust)
/// if the hex length isn't 64 (which will be converted into the 32 bytes) it will return an error.
pub fn from_hex(hex: &str) -> Result<Self, FromHexError> {
if hex.len() != 64 {
return Err(FromHexError::InvalidHexLength);
Expand All @@ -21,6 +42,7 @@ impl Hash256 {
Ok(result)
}

/// Checks if the struct contains only zeroes or not.
pub fn is_zero(&self) -> bool {
self.0 == [0u8;32]
}
Expand Down Expand Up @@ -65,6 +87,16 @@ impl AsMut<[u8]> for Hash256 {
}
}


/// The only reason why we need to implement Serialize/Deserialize ourselves is because the derive macro
/// contains `extern crate serde as _serde` but we renamed serde. so that's invalid. https://github.com/serde-rs/serde/pull/1499
/// We had to rename it to support 2 different serde implementations, 1 from crates.io and another from baidu.
///
/// I tried to keep this implementation as simple as possible and as close as possible to the auto generated one <br>
/// (you can see the autogenerated one by running
/// `cargo rustc --profile=check -- -Zunstable-options --pretty=expanded` on a code with the derive macro)

use crate::serde::de::{SeqAccess, Error};
use core::fmt::{self, Formatter};
use crate::serde::{Serialize, Deserialize, Serializer, Deserializer, de::Visitor};
Expand Down
16 changes: 15 additions & 1 deletion enigma-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![deny(unused_extern_crates)]
#![deny(unused_extern_crates, missing_docs, warnings)]
//! # Enigma Types
//! This library is meant to supply all the types that are specific to our protocol. <br>
//! Inside of this library I abstracted the std as `localstd` so that you can use it without knowing if it's `sgx_tstd` or regular std.
//! *But* Unlike other crates this isn't just abstracting 2 different std's,
//! but this crate is expected to work even without std at all(except some parts maybe).
//!
//! in the `build.rs` I use `cbindgen` to auto generate `enigma-types.h` header so it can be included into the edl.
//! that way we can pass rust structs through the SGX bridge(which is C)
//!
//! This crate is Rust 2018 Edition,
//! meaning there's no `extern crate` and `use` statements need to start with `crate`/`self`/`super`.

pub mod traits;
Expand All @@ -15,6 +26,9 @@ use serde_std as serde;
use crate::traits::SliceCPtr;
pub use crate::types::*;

/// This is a bit safer wrapper of [`core::ptr::copy_nonoverlapping`]
/// it checks that the src len is at least as bigger as `count` otherwise it will panic.
/// *and* it uses [`enigma_types::traits::SliceCPtr`] trait to pass a C compatible pointer.
pub unsafe fn write_ptr<T>(src: &[T], dst: *mut T, count: usize) {
if src.len() > count {
unimplemented!()
Expand Down
17 changes: 17 additions & 0 deletions enigma-types/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
//! # Traits module
//! This module should provide low level traits that are required on both sides of the SGX.
//! right now it only contains the [`SliceCPtr`] trait which is used to *always* provide valid C pointers.
static EMPTY: [u8; 1] = [0];

/// This trait provides an interface into `C` like pointers.
/// in Rust if you try to get a pointer to an empty vector you'll get:
/// 0x0000000000000001 OR 0x0000000000000000, although bear in mind this *isn't* officially defined.
/// this behavior is UB in C's `malloc`, passing an invalid pointer with size 0 to `malloc` is implementation defined.
/// in the case of Intel's + GCC what we observed is a Segmentation Fault.
/// this is why if the vec/slice is empty we use this trait to pass a pointer to a stack allocated static `[0]` array.
/// this will make the pointer valid, and when the len is zero
/// `malloc` won't allocate anything but also won't produce a SegFault
pub trait SliceCPtr {
/// The Target for the trait.
/// this trait can't be generic because it should only be implemented once per type
/// (See [Associated Types][https://doc.rust-lang.org/rust-by-example/generics/assoc_items/types.html])
type Target;
/// This function is what will produce a valid C pointer to the target
/// even if the target is 0 sized (and rust will produce a C *invalid* pointer for it )
fn as_c_ptr(&self) -> *const Self::Target;
}

Expand Down
88 changes: 88 additions & 0 deletions enigma-types/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,113 @@
//! # Types module
//! This module should provide low level types, enums and structures that are required on both sides of the SGX.
//! All enums and structures should have `#[repr(C)]` on them so they would be aligned just like in C
//! See [`Repr(C)`][https://doc.rust-lang.org/nomicon/other-reprs.html]
//!
//! Any new type here that should pass through the edl should be added into the [`build.rs]` file,
//! so it will put it into the auto generated C header.
//!
//! Note: Please use the right types even if they're only aliases right now,
//! this helps both for readability and if in the future we decide to change the alias.
use core::{fmt, mem, ptr, default::Default};

pub use crate::hash::Hash256;
/// SymmetricKey is the 256bit symmetric key we use for encryption.
pub type SymmetricKey = [u8; 32];
/// StateKey is the key used for state encryption.
pub type StateKey = SymmetricKey;
/// DHKey is the key that results from the ECDH [`enigma_crypto::KeyPair::derive_key`]
pub type DhKey = SymmetricKey;
/// ContractAddress is the address of contracts in the Enigma Network.
pub type ContractAddress = Hash256;
/// PubKey is a public key that is used for ECDSA signing.
pub type PubKey = [u8; 64];


/// This enum is used to return from an ecall/ocall to represent if the operation was a success and if not then what was the error.
/// The goal is to not reveal anything sensitive
/// `#[repr(C)]` is a Rust feature which makes the struct be aligned just like C structs.
/// See [`Repr(C)`][https://doc.rust-lang.org/nomicon/other-reprs.html]
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EnclaveReturn {
/// Success, the function returned without any failure.
Success,
/// TaskFailure, the task(Deploy/Compute) has failed
TaskFailure,
/// KeysError, There's a key missing or failed to derive a key.
KeysError,
/// Failure in Encryption, couldn't decrypt the variable / failed to encrypt the results.
EncryptionError,
// TODO: I'm not sure this error is used anywhere.
/// SigningError, for some reason it failed on signing the results.
SigningError,
// TODO: Also don't think this is needed.
/// RecoveringError, Something failed in recovering the public key.
RecoveringError,
///PermissionError, Received a permission error from an ocall, (i.e. opening the signing keys file or somethign like that)
PermissionError,
/// SgxError, Error that came from the SGX specific stuff (i.e DRAND, Sealing etc.)
SgxError,
/// StateError, an Error in the State. (i.e. failed applying delta, failed deserializing it etc.)
StateError,
/// OcallError, an error from an ocall.
OcallError,
/// OcallDBError, an error from the Database in the untrusted part, could't get/save something.
OcallDBError,
/// MessagingError, a message that received couldn't be processed (i.e. KM Message, User Key Exchange etc.)
MessagingError,
/// WorkerAuthError, Failed to authenticate the worker, this is specific to the KM node.
WorkerAuthError,
// TODO: should consider merging with a different error.
/// Missing StateKeys in the KM node.
KeyProvisionError,
/// Something went really wrong.
Other
}


/// This struct is basically some sort of a boolean that says if an operation was a success or a failure.
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum ResultStatus {
/// Ok = Success = 1.
Ok = 1,
/// Failure = Error = 0.
Failure = 0,
}


/// This struct is what returned from a Deploy/Compute ecall, it contains all the needed data.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ExecuteResult {
/// A pointer to the output of the execution using [`ocall_save_to_memory`] (on the untrusted stack)
pub output: *const u8,
/// A pointer to the resulting delta using [`ocall_save_to_memory`] (on the untrusted stack)
pub delta_ptr: *const u8,
/// The delta index number.
pub delta_index: u32,
/// A pointer to the Ethereum payload using [`ocall_save_to_memory`] (on the untrusted stack)
pub ethereum_payload_ptr: *const u8,
/// The ethereum address that the payload belongs to.
pub ethereum_address: [u8; 20],
/// A signature by the enclave on all of the results.
pub signature: [u8; 65],
/// The gas used by the execution.
pub used_gas: u64,
}

/// This struct is a wrapper to a raw pointer.
/// when you pass a pointer through the SGX bridge(EDL) than the SGX Edger8r it copies the data that it's pointing to
/// using `memalloc` and `memset` to the other side of the bridge, then it changes the pointer to point to the new data.
///
/// So this struct is needed if you want to pass a pointer from one side to the other while the pointer still points to the right locaiton.
///
/// Say you want to give the enclave a DB on the untrusted, so that the enclave can then pass that pointer to an ocall.
/// This will let you do it without the Edger8r messing with the pointer.
///
/// And I tried to add a mutability bool to make it a little more safe by giving you a pointer based on the original mutability.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct RawPointer {
Expand All @@ -56,18 +116,26 @@ pub struct RawPointer {
}

impl RawPointer {
/// Creates a new RawPointer wrapper.
/// it will auto cast the reference into a raw pointer.
pub unsafe fn new<T>(reference: &T) -> Self {
RawPointer { ptr: reference as *const T as *const u8, _mut: false }
}

/// Creates a new mutable RawPointer wrapper.
/// This is needed if when you unwrap this you want a mutable pointer.
pub unsafe fn new_mut<T>(reference: &mut T) -> Self {
RawPointer { ptr: reference as *mut T as *const u8, _mut: true }
}

/// This will return the underlying const raw pointer.
pub fn get_ptr<T>(&self) -> *const T {
self.ptr as *const T
}

/// this will return a Result and if the RawPointer was created with `new_mut`
/// it Will return `Ok` with the underlying mut raw pointer.
/// if the struct was created with just `new` it will return `Err`.
pub fn get_mut_ptr<T>(&self) -> Result<*mut T, &'static str> {
if !self._mut {
Err("This DoublePointer is not mutable")
Expand All @@ -76,10 +144,15 @@ impl RawPointer {
}
}

/// This will unsafely cast the underlying pointer back into a reference.
pub unsafe fn get_ref<T>(&self) -> &T {
&*(self.ptr as *const T)
}

/// This will unsafely cast the underlying pointer back into a mut pointer.
/// it will return a result and has the same rules as [`get_mut_ptr`]
///
/// [`get_mut_ptr`]: #method.get_mut_ptr
pub unsafe fn get_mut_ref<T>(&self) -> Result<&mut T, &'static str> {
if !self._mut {
Err("This DoublePointer is not mutable")
Expand Down Expand Up @@ -154,7 +227,22 @@ impl fmt::Display for EnclaveReturn {
}
}


/// This trait will convert a Result into EnclaveReturn.
///
/// I used this because there's a problem.
/// we want to convert [`enigma_tools_t::common::errors::EnclaveError`] into [`EnclaveReturn`] to return it back through the EDL.
/// *but* in this module we can't impl [`std::convert::From`] from `EnclaveError` to `EnclaveReturn` because this crate is `std` pure
/// so it doesn't have access to `enigma_tools_t`.
/// And we can't impelment this as `Into<EncalveReturn> for Result<(), EnclaveError>` in `enigma_tools_t`
/// because in rust you can't implement an imported trait(`From`/`Into`) on a type you imported (`Result`).
///
/// So my solution was to declare a new trait, and to implement [`std::convert::From`] on whatever implements my trait through generics.
/// that way all we need is to implement `ResultToEnclaveReturn` on `EnclaveError` and it will auto generate a `From` impl for it.
///
/// And if the Result is `Ok` it will return `EnclaveReturn::Success` and if `Err` it will convert using this trait.
pub trait ResultToEnclaveReturn {
/// Should return a EnclaveReturn while consuming self.
fn into_enclave_return(self) -> EnclaveReturn;
}

Expand Down

0 comments on commit 19319de

Please sign in to comment.