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

Commit

Permalink
Adding documentation and explanations to some parts of core (#122)
Browse files Browse the repository at this point in the history
Adding documentation and explanations to some parts of core
  • Loading branch information
elichai authored May 29, 2019
2 parents bc68dc0 + 1f5da2d commit c9112fe
Show file tree
Hide file tree
Showing 25 changed files with 401 additions and 19 deletions.
7 changes: 7 additions & 0 deletions enigma-core/app/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! # Enigma Core CLI.
//!
//! We use `StructOpt` to easily generate the CLI https://github.com/TeXitoi/structopt <br>
//! it uses rustdocs for the `--help` menu, and proc macros to get long/short and parsing methods. <br>
//! it is used by running `let opt: Opt = Opt::from_args();` and then it will fill up the struct from the user inputs.
//! (and of course fail if needed)
use std::path::PathBuf;
use structopt::StructOpt;

Expand Down
2 changes: 1 addition & 1 deletion enigma-core/app/src/esgx/ocalls_u.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(unused_attributes)]
use crate::db::{CRUDInterface, DeltaKey, P2PCalls, ResultType, ResultTypeVec, Stype, DB};
use enigma_crypto::hash::Sha256;
use enigma_tools_m::utils::LockExpectMutex;
use enigma_crypto::hash::Sha256;
use enigma_types::{ContractAddress, EnclaveReturn, Hash256, RawPointer};
use lru_cache::LruCache;
use std::sync::Mutex;
Expand Down
3 changes: 3 additions & 0 deletions enigma-crypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 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-crypto"
version = "0.1.0"
Expand Down
34 changes: 33 additions & 1 deletion enigma-crypto/src/asymmetric.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
//! # Asymmetric Cryptography.
//! This module provides an interface to generate Secp256k1 keys, sign and verify signatures. <br>
//! Right now we use https://github.com/sorpaas/libsecp256k1-rs as a backend but this is a less common library,
//! Meaning if we use this in testnet/mainnet we should audit that library ourself.
//! otherwise we need to put effort into using the much audited library: https://github.com/bitcoin-core/secp256k1
//! I put work into making the rust bindings for this library support SGX and after this PR it should be ready:
//! https://github.com/rust-bitcoin/rust-secp256k1/pull/115
//! After this PR I think it would be possible to swap that library in instead of the current one.
//!
//! Here is a PoC of how it can be done easily (and the one problem with it) https://github.com/enigmampc/enigma-core/pull/167
use crate::error::CryptoError;
use secp256k1::{PublicKey, SecretKey, SharedSecret, RecoveryId, Signature};
use crate::hash::Keccak256;
use enigma_types::{DhKey, PubKey};


/// The `KeyPair` struct is used to hold a Private and Public keys.
/// you can use it to sign a message, to derive shared secrets(ECDH) etc.
#[derive(Debug)]
pub struct KeyPair {
pubkey: PublicKey,
privkey: SecretKey,
}

impl KeyPair {
/// This will generate a fresh pair of Public and Private keys.
/// it will use the available randomness from [crate::rand]
#[cfg(any(feature = "sgx", feature = "std"))]
pub fn new() -> Result<KeyPair, CryptoError> {
use crate::rand;
// This loop is important to make sure that the resulting public key isn't a point in infinity(at the curve).
// So if the Resulting public key is bad we need to generate a new random private key and try again until it succeeds.
loop {
let mut me: [u8; 32] = [0; 32];
rand::random(&mut me)?;
Expand All @@ -23,6 +41,10 @@ impl KeyPair {
}
}

/// This function will create a Pair of keys from an array of 32 bytes.
/// Please don't use it to generate a new key, if you want a new key use `KeyPair::new()`
/// Because `KeyPair::new()` will make sure it uses a good random source and will loop private keys until it's a good key.
/// (and it's best to isolate the generation of keys to one place)
pub fn from_slice(privkey: &[u8; 32]) -> Result<KeyPair, CryptoError> {
let privkey = SecretKey::parse(&privkey)
.map_err(|e| CryptoError::KeyError { key_type: "Private Key", err: Some(e) })?;
Expand All @@ -31,6 +53,8 @@ impl KeyPair {
Ok(KeyPair { privkey, pubkey })
}

/// This function does an ECDH(point multiplication) between one's private key and the other one's public key.
///
pub fn derive_key(&self, _pubarr: &PubKey) -> Result<DhKey, CryptoError> {
let mut pubarr: [u8; 65] = [0; 65];
pubarr[0] = 4;
Expand All @@ -47,11 +71,14 @@ impl KeyPair {
Ok(result)
}

/// This will return the raw 32 bytes private key. use carefully.
pub fn get_privkey(&self) -> [u8; 32] { self.privkey.serialize() }

/// Get the Public Key and slice the first byte
/// The first byte represents if the key is compressed or not.
/// Because we always use Uncompressed Keys That's start with `0x04` we can slice it out.
/// Because we use uncompressed Keys That start with `0x04` we can slice it out.
///
/// We should move to compressed keys in the future, this will save 31 bytes on each pubkey.
///
/// See More:
/// `https://tools.ietf.org/html/rfc5480#section-2.2`
Expand Down Expand Up @@ -80,6 +107,9 @@ impl KeyPair {
/// 1. 32 Bytes, ECDSA `r` variable.
/// 2. 32 Bytes ECDSA `s` variable.
/// 3. 1 Bytes ECDSA `v` variable aligned to the right for Ethereum compatibility
///
/// The `v` variable or so called `Recovery ID` is to tell you if the public key that's needed to verify is even or odd. <br>
/// Ususally that byte is just 0/1 for some reasons these are represented as 0/1 so we just add 27 to it.
pub fn sign(&self, message: &[u8]) -> Result<[u8; 65], CryptoError> {
let hashed_msg = message.keccak256();
let message_to_sign = secp256k1::Message::parse(&hashed_msg);
Expand Down Expand Up @@ -110,9 +140,11 @@ impl KeyPair {
let signature = Signature::parse_slice(&sig[..64])
.map_err(|_| CryptoError::ParsingError { sig } )?;
let hashed_msg = message.keccak256();

let signed_message = secp256k1::Message::parse(&hashed_msg);
let recovered_pub = secp256k1::recover(&signed_message, &signature, &recovery)
.map_err(|_| CryptoError::RecoveryError { sig } )?;

Ok(KeyPair::pubkey_object_to_pubkey(&recovered_pub))
}

Expand Down
38 changes: 38 additions & 0 deletions enigma-crypto/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,59 @@
//! # Errors
//! This module contains the `CryptoError` enum which is the used error type in this crate.
//!
use failure::Fail;
use crate::localstd::fmt;

/// This Error enum tries to give the exact explanation of the error *without* revealing any secrets.
/// I tried to minimize the usage of cfg conditions here and have unified errors for sgx and std as much as I could.
#[allow(missing_docs)]
#[derive(Fail)]
#[cfg_attr(feature = "sgx", derive(Clone))]
pub enum CryptoError {
/// The `DerivingKeyError` error.
///
/// This error means that the ECDH process failed.
DerivingKeyError { self_key: [u8; 64], other_key: [u8; 64] },
/// The `MissingKeyError` error.
///
/// This error means that a key was missing.
MissingKeyError { key_type: &'static str },
/// The `DecryptionError` error.
///
/// This error means that the symmetric decryption has failed for some reason.
DecryptionError,
/// The `ImproperEncryption` error.
///
/// This error means that the ciphertext provided was imporper.
/// e.g. MAC wasn't valid, missing IV etc.
ImproperEncryption,
/// The `EncryptionError` error.
///
/// This error means that the symmetric encryption has failed for some reason.
EncryptionError,
/// The `SigningError` error.
///
/// This error means that the signing process has failed for some reason.
SigningError { hashed_msg: [u8; 32] },
/// The `ParsingError` error.
///
/// This error means that the signature couldn't be parsed correctly.
ParsingError { sig: [u8; 65] },
/// The `RecoveryError` error.
///
/// This error means that the public key can't be recovered from that message & signature.
RecoveryError { sig: [u8; 65] },
/// The `KeyError` error.
///
/// This error means that a key wasn't vaild.
/// e.g. PrivateKey, PubliKey, SharedSecret.
#[cfg(feature = "asymmetric")]
KeyError { key_type: &'static str, err: Option<secp256k1::Error> },
#[cfg(not(feature = "asymmetric"))]
KeyError { key_type: &'static str, err: Option<()> },
/// The `RandomError` error.
///
/// This error means that the random function had failed generating randomness.
#[cfg(feature = "std")]
RandomError { err: rand_std::Error },
#[cfg(feature = "sgx")]
Expand Down
9 changes: 8 additions & 1 deletion enigma-crypto/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! # Hash Module
//! This module provides Keccak256 and Sha256 implementations as traits for all slices.
//! I think we should consider removing the Sha256 implementation to make sure we use the same hash function always.
use tiny_keccak::Keccak;
use enigma_types::Hash256;

Expand Down Expand Up @@ -26,12 +30,15 @@ pub fn prepare_hash_multiple<B: AsRef<[u8]>>(messages: &[B]) -> crate::localstd:
res
}

// Hash a byte array into keccak256.
/// A trait that will hash using Keccak256 the object it's implemented on.
pub trait Keccak256<T> {
/// This will return a sized object with the hash
fn keccak256(&self) -> T where T: Sized;
}

/// A trait that will hash using Sha256 the object it's implemented on.
pub trait Sha256<T> {
/// This will return a sized object with the hash
fn sha256(&self) -> T where T: Sized;
}

Expand Down
22 changes: 21 additions & 1 deletion enigma-crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![deny(unused_extern_crates)]
#![deny(unused_extern_crates, missing_docs, warnings)]
//! # Enigma Crypto
//! This library is a wrapper for all of our cryptographic needs. <br>
//! No crypto (encryption/hashing/signing etc.) should be used directly. everything should go through this library. <br>
//! This library can work on both sides of the SGX through the use of compilation cfg's. <br>
//! It also works in WASM, but without the symmetric encryption, because `ring` uses AES-NI instructions which are x86(64) only. <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.
//!
//! This crate is Rust 2018 Edition,
//! meaning there's no `extern crate` and `use` statements need to start with `crate`/`self`/`super`.
#[cfg(feature = "asymmetric")]
pub mod asymmetric;
Expand Down Expand Up @@ -31,10 +41,20 @@ pub use crate::error::CryptoError;
pub use crate::asymmetric::KeyPair;



/// This trait is to encrypt/decrypt a struct, when implemented you should use `symmetric::encrypt`.
/// when you implement decrypt and encrypt_with_nonce you get `encrypt` for free(don't need to implement manually).
/// you should only use decrypt/encrypt. `encrypt_with_nonce` is for testing purposes only.
pub trait Encryption<T, E, R, N>
where R: Sized, Self: Sized {
/// the `encrypt` function is given for free
#[allow(deprecated)]
fn encrypt(self, key: T) -> Result<R, E> { self.encrypt_with_nonce(key, None) }
#[deprecated(note = "This function shouldn't be called directly, please use `encrypt()` instead")]
/// This function is to encrypt the object using the given key. it shouldn't be used directly.
/// It should only be implemented in order for you to get the normal `encrypt` function.
fn encrypt_with_nonce(self, key: T, _iv: Option<N>) -> Result<R, E>;
/// This function will decrypt the encrypted struct using `encrypt` into the same object.
fn decrypt(enc: R, key: T) -> Result<Self, E>;
}

19 changes: 13 additions & 6 deletions enigma-crypto/src/rand.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
use crate::CryptoError;
//! # Rand module
//! This module is an abstraction layer over the random functions. <br>
//! The purpose of it is so it can be used the same within and outside of SGX
//! (through `/dev/urandom` and through the `RDRAND` instruction.)

#[cfg(all(feature = "std", not(feature = "sgx")))]
pub fn random(rand: &mut [u8]) -> Result<(), CryptoError> {
/// This function gets a mutable slice and will fill it
/// with random data using the available randomness source
pub fn random(rand: &mut [u8]) -> Result<(), crate::CryptoError> {
use rand_std::{Rng, rngs::EntropyRng};
let mut rng = EntropyRng::new();
rng.try_fill(rand)
.map_err(|e| CryptoError::RandomError { err: e } )
.map_err(|e| crate::CryptoError::RandomError { err: e } )
}


#[cfg(all(feature = "sgx", not(feature = "std")))]
pub fn random(rand: &mut [u8]) -> Result<(), CryptoError> {
/// This function gets a mutable slice and will fill it
/// with random data using the available randomness source
pub fn random(rand: &mut [u8]) -> Result<(), crate::CryptoError> {
use sgx_trts::trts::rsgx_read_rand;
rsgx_read_rand(rand)
.map_err(|e| CryptoError::RandomError { err: e } )
.map_err(|e| crate::CryptoError::RandomError { err: e } )
}
17 changes: 16 additions & 1 deletion enigma-crypto/src/symmetric.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
//! # Symmetric Cryptography.
//!
//! This module provides an interface to encrypting data. <br>
//! Right now we use AES-256-GCM as an AEAD (Authenticated Encryption). <br>
//! We use the Ring library which uses some BoringSSL code and mostly AES-NI inline ASM instructions. <br>
//! Right now I have a fork of ring which gives us SGX and no-sgx access via rust features and C compilation flags. <br>
//!
use enigma_types::SymmetricKey;
use crate::error::CryptoError;
use ring::aead::{self, Nonce, Aad};
Expand All @@ -12,8 +20,13 @@ static AES_MODE: &aead::Algorithm = &aead::AES_256_GCM;
type IV = [u8; IV_SIZE];


/// This function get's a key and a slice of data and encrypts the data using the key.
/// the IV/nonce is appended to the cipher text after the MAC tag.
pub fn encrypt(message: &[u8], key: &SymmetricKey) -> Result<Vec<u8>, CryptoError> { encrypt_with_nonce(message, key, None) }

//#[deprecated(note = "This function shouldn't be called directly unless you're implementing the Encryption trait, please use `encrypt()` instead")]
/// This function does the same as [`self::encrypt`] but accepts an IV.
/// it *shouldn't* be called directly. only from tests or [`crate::Encryption::encrypt_with_nonce`] implementations.
pub fn encrypt_with_nonce(message: &[u8], key: &SymmetricKey, _iv: Option<IV>) -> Result<Vec<u8>, CryptoError> {
let iv = match _iv {
Some(x) => x,
Expand All @@ -40,7 +53,9 @@ pub fn encrypt_with_nonce(message: &[u8], key: &SymmetricKey, _iv: Option<IV>) -
Ok(in_out)
}


/// This function will decrypt a cipher text only if it was encrypted with the `encrypt` function above.
/// Because it will try to get the IV from the last 12 bytes in the cipher text,
/// then ring will take the last 16 bytes as a MAC to check the integrity of the cipher text.
pub fn decrypt(cipheriv: &[u8], key: &SymmetricKey) -> Result<Vec<u8>, CryptoError> {
if cipheriv.len() < IV_SIZE {
return Err(CryptoError::ImproperEncryption);
Expand Down
2 changes: 2 additions & 0 deletions enigma-tools-m/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-tools-m"
Expand Down
15 changes: 14 additions & 1 deletion enigma-tools-m/src/common/errors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
//! # Errors
//! This is a module for the errors of this crate.
//! we use Failure to handle the Error and Display traits and other conversions.
//!
use failure::Fail;

/// This Error enum is used to represent errors from this library.
/// Pro tip: If you want to add a string message to the error and you always hard code it,
/// then you can use `&'static str` instead of String, this will make your code much nicer.
#[derive(Debug, Fail, Clone)]
pub enum ToolsError {
/// The `MessagingError` error.
///
/// This error means that there was a Messaging problem (e.g. couldn't deserialize a message)
#[fail(display = "There's an error with the messaging: {}", err)]
MessagingError { err: &'static str },
MessagingError {
/// `Err` is the custom message that should explain what and where was the problem.
err: &'static str
},
}
12 changes: 11 additions & 1 deletion enigma-tools-m/src/common/utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//! # Mutual Utils.
//! This module contain some handy utils.
//! Right now only a trait that can convert `[u8; 64]` to a 20 bytes Ethereum address
//! or a 20 bytes Ethereum address String in hex representation.
use crate::localstd::string::String;
use enigma_crypto::hash::Keccak256;
use rustc_hex::ToHex;
Expand All @@ -8,18 +13,23 @@ use crate::localstd::sync::{SgxMutex as Mutex, SgxMutexGuard as MutexGuard};
#[cfg(feature = "std")]
use crate::localstd::sync::{Mutex, MutexGuard};


/// A trait that is basically a shortcut for `mutex.lock().expect(format!("{} mutex is posion", name))`
/// you instead call `mutex.lock_expect(name)` and it will act the same.
pub trait LockExpectMutex<T> {
/// See trait documentation. a shortcut for `lock()` and `expect()`
fn lock_expect(&self, name: &str) -> MutexGuard<T>;
}

impl<T> LockExpectMutex<T> for Mutex<T> {
fn lock_expect(&self, name: &str) -> MutexGuard<T> { self.lock().unwrap_or_else(|_| panic!("{} mutex is poison", name)) }
}

/// A trait to convert an object into an Ethereum Address
pub trait EthereumAddress<T, P> {
/// This should convert the object(by hashing and slicing) into a String type 40 characters Ethereum address.
fn address_string(&self) -> T
where T: Sized;
/// This should convert the object(by hashing and slicing) into a 20 byte Ethereum address.
fn address(&self) -> P
where P: Sized;
}
Expand Down
2 changes: 2 additions & 0 deletions enigma-tools-m/src/keeper_types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(missing_docs)] // This should be removed after @fredfortier will document this module.

use crate::localstd::{vec, vec::Vec};
use log::debug;
use log_derive::logfn;
Expand Down
8 changes: 7 additions & 1 deletion enigma-tools-m/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![deny(unused_extern_crates)]
#![deny(unused_extern_crates, missing_docs)]
//! # Enigma Tools Mutual
//! This library provides tools for both untrusted and trusted sides of the SGX. <br>
//! It should supersede both `enigma-tools-t` and `enigma-tools-u`. <br?
//! it abstracts std as `localstd` to and a lot of other library are abstracted via cfg conditions. <br>
//! This crate is Rust 2018 Edition,
//! meaning there's no `extern crate` and `use` statements need to start with `crate`/`self`/`super`.
mod common;
pub mod keeper_types;
Expand Down
Loading

0 comments on commit c9112fe

Please sign in to comment.