// Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materials provided with the
//    distribution.
//  * Neither the name of Baidu, Inc., nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//!
//! Cryptographic Functions
//!
use sgx_types::*;
use sgx_types::marker::ContiguousMemory;
use std::ops::{Drop, DerefMut};
use std::ptr;
use std::mem;
use std::cell::{Cell, RefCell};

///
/// The rsgx_sha256_msg function performs a standard SHA256 hash over the input data buffer.
///
/// # Description
///
/// The rsgx_sha256_msg function performs a standard SHA256 hash over the input data buffer.
/// Only a 256-bit version of the SHA hash is supported. (Other sizes, for example 512, are
/// not supported in this minimal cryptography library).
///
/// The function should be used if the complete input data stream is available.
/// Otherwise, the Init, Update… Update, Final procedure should be used to compute
/// a SHA256 bit hash over multiple input data sets.
///
/// # Parameters
///
/// **src**
///
/// A pointer to the input data stream to be hashed.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Return value
///
/// The 256-bit hash that has been SHA256 calculated
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// Input pointers are invalid.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// The SHA256 hash calculation failed.
///
pub fn rsgx_sha256_msg<T>(src: &T) -> SgxResult<sgx_sha256_hash_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut hash = sgx_sha256_hash_t::default();
    let ret = unsafe { sgx_sha256_msg(src as * const _ as * const u8, size as u32, &mut hash as * mut sgx_sha256_hash_t) };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(hash),
        _ => Err(ret),
    }
}

///
/// The rsgx_sha256_slice function performs a standard SHA256 hash over the input data buffer.
///
pub fn rsgx_sha256_slice<T>(src: &[T]) -> SgxResult<sgx_sha256_hash_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut hash = sgx_sha256_hash_t::default();
    let ret = unsafe { sgx_sha256_msg(src.as_ptr() as * const u8, size as u32, &mut hash as * mut sgx_sha256_hash_t) };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(hash),
        _ => Err(ret),
    }
}

fn rsgx_sha256_init(sha_handle: &mut sgx_sha_state_handle_t) -> sgx_status_t {

    unsafe {
        sgx_sha256_init(sha_handle as * mut sgx_sha_state_handle_t)
    }
}

fn rsgx_sha256_update_msg<T>(src: &T, sha_handle: sgx_sha_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        sgx_sha256_update(src as * const _ as * const u8, size as u32, sha_handle)
    }
}

fn rsgx_sha256_update_slice<T>(src: &[T], sha_handle: sgx_sha_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    unsafe {
        sgx_sha256_update(src.as_ptr() as * const u8, size as u32, sha_handle)
    }
}

fn rsgx_sha256_get_hash(sha_handle: sgx_sha_state_handle_t, hash: &mut sgx_sha256_hash_t) -> sgx_status_t {

    unsafe { sgx_sha256_get_hash(sha_handle, hash as * mut sgx_sha256_hash_t) }
}

fn rsgx_sha256_close(sha_handle: sgx_sha_state_handle_t) -> sgx_status_t {

     unsafe { sgx_sha256_close(sha_handle) }
}

pub fn rsgx_sha1_msg<T>(src: &T) -> SgxResult<sgx_sha1_hash_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut hash = sgx_sha1_hash_t::default();
    let ret = unsafe { sgx_sha1_msg(src as * const _ as * const u8, size as u32, &mut hash as * mut sgx_sha1_hash_t) };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(hash),
        _ => Err(ret),
    }
}

pub fn rsgx_sha1_slice<T>(src: &[T]) -> SgxResult<sgx_sha1_hash_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut hash = sgx_sha1_hash_t::default();
    let ret = unsafe { sgx_sha1_msg(src.as_ptr() as * const u8, size as u32, &mut hash as * mut sgx_sha1_hash_t) };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(hash),
        _ => Err(ret),
    }
}

fn rsgx_sha1_init(sha_handle: &mut sgx_sha_state_handle_t) -> sgx_status_t {

    unsafe {
        sgx_sha1_init(sha_handle as * mut sgx_sha_state_handle_t)
    }
}

fn rsgx_sha1_update_msg<T>(src: &T, sha_handle: sgx_sha_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        sgx_sha1_update(src as * const _ as * const u8, size as u32, sha_handle)
    }
}

fn rsgx_sha1_update_slice<T>(src: &[T], sha_handle: sgx_sha_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    unsafe {
        sgx_sha1_update(src.as_ptr() as * const u8, size as u32, sha_handle)
    }
}

fn rsgx_sha1_get_hash(sha_handle: sgx_sha_state_handle_t, hash: &mut sgx_sha1_hash_t) -> sgx_status_t {

    unsafe { sgx_sha1_get_hash(sha_handle, hash as * mut sgx_sha1_hash_t) }
}

fn rsgx_sha1_close(sha_handle: sgx_sha_state_handle_t) -> sgx_status_t {

     unsafe { sgx_sha1_close(sha_handle) }
}

///
/// SHA algorithm context state.
///
/// This is a handle to the context state used by the cryptography library to perform an iterative SHA256 hash.
/// The algorithm stores the intermediate results of performing the hash calculation over data sets.
///
pub struct SgxShaHandle {
    handle: RefCell<sgx_sha_state_handle_t>,
    initflag: Cell<bool>,
}

impl SgxShaHandle {

    ///
    /// Constructs a new, empty SgxShaHandle.
    ///
    pub fn new() -> Self {
        SgxShaHandle{
            handle: RefCell::new(ptr::null_mut() as sgx_sha_state_handle_t),
            initflag: Cell::new(false),
        }
    }

    ///
    /// init returns an allocated and initialized SHA algorithm context state.
    ///
    /// This should be part of the Init, Update … Update, Final process when the SHA hash is to be performed
    /// over multiple datasets. If a complete dataset is available, the recommend call is rsgx_sha256_msg to
    /// perform the hash in a single call.
    ///
    /// # Description
    ///
    /// Calling init is the first set in performing a SHA256 hash over multiple datasets. The caller does not
    /// allocate memory for the SHA256 state that this function returns. The state is specific to the implementation
    /// of the cryptography library; thus the allocation is performed by the library itself. If the hash over the
    /// desired datasets is completed or any error occurs during the hash calculation process, sgx_sha256_close should
    /// be called to free the state allocated by this algorithm.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The SHA256 state is not initialized properly due to an internal cryptography library failure.
    ///
    pub fn init(&self) -> SgxError {

        if self.initflag.get() {
            return Ok(());
        }

        let ret = rsgx_sha256_init(self.handle.borrow_mut().deref_mut());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(true);
                Ok(())
            },
            _ => Err(ret),
        }
    }

    ///
    /// update_msg performs a SHA256 hash over the input dataset provided.
    ///
    /// This function supports an iterative calculation of the hash over multiple datasets where the
    /// sha_handle contains the intermediate results of the hash calculation over previous datasets.
    ///
    /// # Description
    ///
    /// This function should be used as part of a SHA256 calculation over multiple datasets.
    /// If a SHA256 hash is needed over a single data set, function rsgx_sha256_msg should be used instead.
    /// Prior to calling this function on the first dataset, the init function must be called first to allocate
    /// and initialize the SHA256 state structure which will hold intermediate hash results over earlier datasets.
    /// The function get_hash should be used to obtain the hash after the final dataset has been processed
    /// by this function.
    ///
    /// # Parameters
    ///
    /// **src**
    ///
    /// A pointer to the input data stream to be hashed.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The SHA256 state is not initialized.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// An internal cryptography library failure occurred while performing the SHA256 hash calculation.
    ///
    pub fn update_msg<T>(&self, src: &T) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_sha256_update_msg(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    ///
    /// update_slice performs a SHA256 hash over the input dataset provided.
    ///
    pub fn update_slice<T>(&self, src: &[T]) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_sha256_update_slice(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    ///
    /// get_hash obtains the SHA256 hash after the final dataset has been processed.
    ///
    /// # Description
    ///
    /// This function returns the hash after performing the SHA256 calculation over one or more datasets
    /// using the update function.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Return value
    ///
    /// The 256-bit hash that has been SHA256 calculated
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The SHA256 state is not initialized.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The SHA256 state passed in is likely problematic causing an internal cryptography library failure.
    ///
    pub fn get_hash(&self) -> SgxResult<sgx_sha256_hash_t> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut hash = sgx_sha256_hash_t::default();
        let ret = rsgx_sha256_get_hash(*self.handle.borrow(), &mut hash);
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(hash),
            _ => Err(ret),
        }
    }

    ///
    /// close cleans up and deallocates the SHA256 state that was allocated in function init.
    ///
    /// # Description
    ///
    /// Calling close is the last step after performing a SHA256 hash over multiple datasets.
    /// The caller uses this function to deallocate memory used to store the SHA256 calculation state.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The input handle is invalid.
    ///
    pub fn close(&self) -> SgxError {

        if !self.initflag.get() {
            return Ok(());
        }

        let ret = {
            let handle = *self.handle.borrow();
            if handle.is_null() {
                sgx_status_t::SGX_SUCCESS
            } else {
                rsgx_sha256_close(handle)
            }
        };

        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(false);
                *self.handle.borrow_mut() = ptr::null_mut();
                Ok(())
            },
            _ => Err(ret),
        }
    }
}

impl Default for SgxShaHandle {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxShaHandle {

    ///
    /// drop cleans up and deallocates the SHA256 state that was allocated in function init.
    ///
    fn drop(&mut self) {
        let _ = self.close();
    }
}

pub struct SgxSha1Handle {
    handle: RefCell<sgx_sha_state_handle_t>,
    initflag: Cell<bool>,
}

impl SgxSha1Handle {

    pub fn new() -> Self {
        SgxSha1Handle{
            handle: RefCell::new(ptr::null_mut() as sgx_sha_state_handle_t),
            initflag: Cell::new(false),
        }
    }

    pub fn init(&self) -> SgxError {

        if self.initflag.get() {
            return Ok(());
        }

        let ret = rsgx_sha1_init(self.handle.borrow_mut().deref_mut());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(true);
                Ok(())
            },
            _ => Err(ret),
        }
    }

    pub fn update_msg<T>(&self, src: &T) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_sha1_update_msg(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    pub fn update_slice<T>(&self, src: &[T]) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_sha1_update_slice(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    pub fn get_hash(&self) -> SgxResult<sgx_sha1_hash_t> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut hash = sgx_sha1_hash_t::default();
        let ret = rsgx_sha1_get_hash(*self.handle.borrow(), &mut hash);
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(hash),
            _ => Err(ret),
        }
    }

    pub fn close(&self) -> SgxError {

        if !self.initflag.get() {
            return Ok(());
        }

        let ret = {
            let handle = *self.handle.borrow();
            if handle.is_null() {
                sgx_status_t::SGX_SUCCESS
            } else {
                rsgx_sha1_close(handle)
            }
        };

        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(false);
                *self.handle.borrow_mut() = ptr::null_mut();
                Ok(())
            },
            _ => Err(ret),
        }
    }
}

impl Default for SgxSha1Handle {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxSha1Handle {

    ///
    /// drop cleans up and deallocates the SHA256 state that was allocated in function init.
    ///
    fn drop(&mut self) {
        let _ = self.close();
    }
}

///
/// rsgx_rijndael128GCM_encrypt performs a Rijndael AES-GCM encryption operation.
///
/// Only a 128bit key size is supported by this Intel(R) SGX SDK cryptography library.
///
/// # Description
///
/// The Galois/Counter Mode (GCM) is a mode of operation of the AES algorithm.
/// GCM [NIST SP 800-38D] uses a variation of the counter mode of operation for
/// encryption. GCM assures authenticity of the confidential data (of up to about
/// 64 GB per invocation) using a universal hash function defined over a binary
/// finite field (the Galois field).
///
/// GCM can also provide authentication assurance for additional data (of practically
/// unlimited length per invocation) that is not encrypted. GCM provides
/// stronger authentication assurance than a (non-cryptographic) checksum or
/// error detecting code. In particular, GCM can detect both accidental modifications
/// of the data and intentional, unauthorized modifications.
///
/// It is recommended that the source and destination data buffers are allocated
/// within the enclave. The AAD buffer could be allocated within or outside
/// enclave memory. The use of AAD data buffer could be information identifying
/// the encrypted data since it will remain in clear text.
///
/// # Parameters
///
/// **key**
///
/// A pointer to key to be used in the AES-GCM encryption operation. The size must be 128 bits.
///
/// **src**
///
/// A pointer to the input data stream to be encrypted. Buffer content could be empty if there is AAD text.
///
/// **iv**
///
/// A pointer to the initialization vector to be used in the AES-GCM calculation. NIST AES-GCM recommended
/// IV size is 96 bits (12 bytes).
///
/// **aad**
///
/// A pointer to an optional additional authentication data buffer which is used in the GCM MAC calculation.
/// The data in this buffer will not be encrypted. The field is optional and content could be empty.
///
/// **dst**
///
/// A pointer to the output encrypted data buffer. This buffer should be allocated by the calling code.
///
/// **mac**
///
/// This is the output GCM MAC performed over the input data buffer (data to be encrypted) as well as
/// the additional authentication data (this is optional data). The calling code should allocate this buffer.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// If both source buffer and AAD buffer content are empty.
///
/// If IV Length is not equal to 12 (bytes).
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An internal cryptography library failure occurred.
///
pub fn rsgx_rijndael128GCM_encrypt(key: &sgx_aes_gcm_128bit_key_t,
                                   src: &[u8],
                                   iv: &[u8],
                                   aad: &[u8],
                                   dst: &mut [u8],
                                   mac: &mut sgx_aes_gcm_128bit_tag_t) -> SgxError {

    let src_len = src.len();
    if src_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let iv_len = iv.len();
    if iv_len != SGX_AESGCM_IV_SIZE {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let aad_len = aad.len();
    if aad_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let dst_len = dst.len();
    if dst_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if dst_len < src_len {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        let p_aad = if aad_len != 0 { aad.as_ptr() } else { ptr::null() };

        let (mut p_src, mut p_dst) = if src_len != 0 {
            (src.as_ptr(), dst.as_mut_ptr())
        } else {
            (ptr::null(), ptr::null_mut())
        };

        sgx_rijndael128GCM_encrypt(key as * const sgx_aes_gcm_128bit_key_t,
                                   p_src,
                                   src_len as u32,
                                   p_dst,
                                   iv.as_ptr(),
                                   iv_len as u32,
                                   p_aad,
                                   aad_len as u32,
                                   mac as * mut sgx_aes_gcm_128bit_tag_t)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

///
/// rsgx_rijndael128GCM_decrypt performs a Rijndael AES-GCM decryption operation.
///
/// Only a 128bit key size is supported by this Intel(R) SGX SDK cryptography library.
///
/// # Description
///
/// The Galois/Counter Mode (GCM) is a mode of operation of the AES algorithm.
/// GCM [NIST SP 800-38D] uses a variation of the counter mode of operation for
/// encryption. GCM assures authenticity of the confidential data (of up to about
/// 64 GB per invocation) using a universal hash function defined over a binary
/// finite field (the Galois field).
///
/// GCM can also provide authentication assurance for additional data (of practically
/// unlimited length per invocation) that is not encrypted. GCM provides
/// stronger authentication assurance than a (non-cryptographic) checksum or
/// error detecting code. In particular, GCM can detect both accidental modifications
/// of the data and intentional, unauthorized modifications.
///
/// It is recommended that the destination data buffer is allocated within the
/// enclave. The AAD buffer could be allocated within or outside enclave memory.
///
/// # Parameters
///
/// **key**
///
/// A pointer to key to be used in the AES-GCM decryption operation. The size must be 128 bits.
///
/// **src**
///
/// A pointer to the input data stream to be decrypted. Buffer content could be empty if there is AAD text.
///
/// **iv**
///
/// A pointer to the initialization vector to be used in the AES-GCM calculation. NIST AES-GCM recommended
/// IV size is 96 bits (12 bytes).
///
/// **aad**
///
/// A pointer to an optional additional authentication data buffer which is provided for the GCM MAC calculation
/// when encrypting. The data in this buffer was not encrypted. The field is optional and content could be empty.
///
/// **mac**
///
/// This is the GCM MAC that was performed over the input data buffer (data to be encrypted) as well as
/// the additional authentication data (this is optional data) during the encryption process (call to
/// rsgx_rijndael128GCM_encrypt).
///
/// **dst**
///
/// A pointer to the output decrypted data buffer. This buffer should be allocated by the calling code.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// If both source buffer and AAD buffer content are empty.
///
/// If IV Length is not equal to 12 (bytes).
///
/// **SGX_ERROR_MAC_MISMATCH**
///
/// The input MAC does not match the MAC calculated.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An internal cryptography library failure occurred.
///
pub fn rsgx_rijndael128GCM_decrypt(key: &sgx_aes_gcm_128bit_key_t,
                                   src: &[u8],
                                   iv: &[u8],
                                   aad: &[u8],
                                   mac: &sgx_aes_gcm_128bit_tag_t,
                                   dst: &mut [u8]) -> SgxError {

    let src_len = src.len();
    if src_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let iv_len = iv.len();
    if iv_len != SGX_AESGCM_IV_SIZE {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let aad_len = aad.len();
    if aad_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let dst_len = dst.len();
    if dst_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if dst_len < src_len {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        let mut p_aad = if !aad.is_empty() { aad.as_ptr() } else { ptr::null() };

        let (mut p_src, mut p_dst) = if src_len != 0 {
            (src.as_ptr(), dst.as_mut_ptr())
        } else {
            (ptr::null(), ptr::null_mut())
        };

        sgx_rijndael128GCM_decrypt(key as * const sgx_aes_gcm_128bit_key_t,
                                   p_src,
                                   src_len as u32,
                                   p_dst,
                                   iv.as_ptr(),
                                   iv_len as u32,
                                   p_aad,
                                   aad_len as u32,
                                   mac as * const sgx_aes_gcm_128bit_tag_t)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

///
/// The rsgx_rijndael128_cmac_msg function performs a standard 128bit CMAC hash over the input data buffer.
///
/// # Description
///
/// The rsgx_rijndael128_cmac_msg function performs a standard CMAC hash over the input data buffer.
/// Only a 128-bit version of the CMAC hash is supported.
///
/// The function should be used if the complete input data stream is available.
/// Otherwise, the Init, Update… Update, Final procedure should be used to compute
/// a CMAC hash over multiple input data sets.
///
/// # Parameters
///
/// **key**
///
/// A pointer to key to be used in the CMAC hash operation. The size must be 128 bits.
///
/// **src**
///
/// A pointer to the input data stream to be hashed.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Return value
///
/// The 128-bit hash that has been CMAC calculated
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// The pointer is invalid.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An internal cryptography library failure occurred.
///
pub fn rsgx_rijndael128_cmac_msg<T>(key: &sgx_cmac_128bit_key_t, src: &T) -> SgxResult<sgx_cmac_128bit_tag_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut mac = sgx_cmac_128bit_tag_t::default();
    let ret = unsafe {
        sgx_rijndael128_cmac_msg(key as * const sgx_cmac_128bit_key_t,
                                 src as * const _ as * const u8,
                                 size as u32,
                                 &mut mac as * mut sgx_cmac_128bit_tag_t)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(mac),
        _ => Err(ret),
    }
}

///
/// The rsgx_rijndael128_cmac_slice function performs a standard 128bit CMAC hash over the input data buffer.
///
pub fn rsgx_rijndael128_cmac_slice<T>(key: &sgx_cmac_128bit_key_t, src: &[T]) -> SgxResult<sgx_cmac_128bit_tag_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut mac = sgx_cmac_128bit_tag_t::default();
    let ret = unsafe {
        sgx_rijndael128_cmac_msg(key as * const sgx_cmac_128bit_key_t,
                                 src.as_ptr() as * const u8,
                                 size as u32,
                                 &mut mac as * mut sgx_cmac_128bit_tag_t)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(mac),
        _ => Err(ret),
    }
}

fn rsgx_cmac128_init(key: &sgx_cmac_128bit_key_t, cmac_handle: &mut sgx_cmac_state_handle_t) -> sgx_status_t {

    unsafe {
        sgx_cmac128_init(key as * const sgx_cmac_128bit_key_t,
                         cmac_handle as * mut sgx_cmac_state_handle_t)
    }
}

fn rsgx_cmac128_update_msg<T>(src: &T, cmac_handle: sgx_cmac_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    unsafe {
        sgx_cmac128_update(src as * const _ as * const u8, size as u32, cmac_handle)
    }
}

fn rsgx_cmac128_update_slice<T>(src: &[T], cmac_handle: sgx_cmac_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    unsafe {
        sgx_cmac128_update(src.as_ptr() as * const _ as * const u8, size as u32, cmac_handle)
    }
}

fn rsgx_cmac128_final(cmac_handle: sgx_cmac_state_handle_t, hash: &mut sgx_cmac_128bit_tag_t) -> sgx_status_t {

    unsafe { sgx_cmac128_final(cmac_handle, hash as * mut sgx_cmac_128bit_tag_t) }
}

fn rsgx_cmac128_close(cmac_handle: sgx_cmac_state_handle_t) -> sgx_status_t {

    unsafe { sgx_cmac128_close(cmac_handle) }
}

///
/// CMAC algorithm context state.
///
/// This is a handle to the context state used by the cryptography library to perform an
/// iterative CMAC 128-bit hash. The algorithm stores the intermediate results of performing
/// the hash calculation over data sets.
///
pub struct SgxCmacHandle {
    handle: RefCell<sgx_cmac_state_handle_t>,
    initflag: Cell<bool>,
}

impl SgxCmacHandle {

    ///
    /// Constructs a new, empty SgxCmacHandle.
    ///
    pub fn new() -> Self {
        SgxCmacHandle{
            handle: RefCell::new(ptr::null_mut() as sgx_cmac_state_handle_t),
            initflag: Cell::new(false),
            }
    }

    ///
    /// init returns an allocated and initialized CMAC algorithm context state.
    ///
    /// This should be part of the Init, Update … Update, Final process when the CMAC hash is to be
    /// performed over multiple datasets. If a complete dataset is available, the recommended call
    /// is rsgx_rijndael128_cmac_msg to perform the hash in a single call.
    ///
    /// # Description
    ///
    /// Calling init is the first set in performing a CMAC 128-bit hash over multiple datasets.
    /// The caller does not allocate memory for the CMAC state that this function returns.
    /// The state is specific to the implementation of the cryptography library and thus the
    /// allocation is performed by the library itself. If the hash over the desired datasets is
    /// completed or any error occurs during the hash calculation process, sgx_cmac128_close should
    /// be called to free the state allocated by this algorithm.
    ///
    /// # Parameters
    ///
    /// **key**
    ///
    /// A pointer to key to be used in the CMAC hash operation. The size must be 128 bits.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// An internal cryptography library failure occurred.
    ///
    pub fn init(&self, key: &sgx_cmac_128bit_key_t) -> SgxError {

        if self.initflag.get() {
            return Ok(());
        }

        let ret = rsgx_cmac128_init(key, self.handle.borrow_mut().deref_mut());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(true);
                Ok(())
            },
            _ => Err(ret),
        }
    }

    ///
    /// update_msg performs a CMAC 128-bit hash over the input dataset provided.
    ///
    /// This function supports an iterative calculation of the hash over multiple datasets where the
    /// cmac_handle contains the intermediate results of the hash calculation over previous datasets.
    ///
    /// # Description
    ///
    /// This function should be used as part of a CMAC 128-bit hash calculation over
    /// multiple datasets. If a CMAC hash is needed over a single data set, function
    /// rsgx_rijndael128_cmac128_msg should be used instead. Prior to calling
    /// this function on the first dataset, the init function must be called first to
    /// allocate and initialize the CMAC state structure which will hold intermediate
    /// hash results over earlier datasets. The function get_hash should be used
    /// to obtain the hash after the final dataset has been processed by this function.
    ///
    /// # Parameters
    ///
    /// **src**
    ///
    /// A pointer to the input data stream to be hashed.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The CMAC state is not initialized.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// An internal cryptography library failure occurred while performing the CMAC hash calculation.
    ///
    pub fn update_msg<T>(&self, src: &T) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_cmac128_update_msg(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    ///
    /// update_slice performs a CMAC 128-bit hash over the input dataset provided.
    ///
    pub fn update_slice<T>(&self, src: &[T]) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_cmac128_update_slice(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    ///
    /// get_hash obtains the CMAC 128-bit hash after the final dataset has been processed.
    ///
    /// # Description
    ///
    /// This function returns the hash after performing the CMAC 128-bit hash calculation
    /// over one or more datasets using the update function.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Return value
    ///
    /// The 128-bit hash that has been CMAC calculated
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The CMAC state is not initialized.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The CMAC state passed in is likely problematic causing an internal cryptography library failure.
    ///
    pub fn get_hash(&self) -> SgxResult<sgx_cmac_128bit_tag_t> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut hash = sgx_cmac_128bit_tag_t::default();
        let ret = rsgx_cmac128_final(*self.handle.borrow(), &mut hash);
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(hash),
            _ => Err(ret),
        }
    }

    ///
    /// close cleans up and deallocates the CMAC algorithm context state that was allocated in function init.
    ///
    /// # Description
    ///
    /// Calling close is the last step after performing a CMAC hash over multiple datasets.
    /// The caller uses this function to deallocate memory used for storing the CMAC algorithm context state.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The input handle is invalid.
    ///
    pub fn close(&self) -> SgxError {

        if !self.initflag.get() {
            return Ok(());
        }

        let ret = {
            let handle = *self.handle.borrow();
            if handle.is_null() {
                sgx_status_t::SGX_SUCCESS
            } else {
                rsgx_cmac128_close(handle)
            }
        };

        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(false);
                *self.handle.borrow_mut() = ptr::null_mut();
                Ok(())
            },
            _ => Err(ret),
        }
    }
}

impl Default for SgxCmacHandle {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxCmacHandle {
    ///
    /// drop cleans up and deallocates the CMAC algorithm context state that was allocated in function init.
    ///
    fn drop(&mut self) {
       let _ = self.close();
    }
}

pub fn rsgx_hmac_sha256_msg<T>(key: &sgx_hmac_256bit_key_t, src: &T) -> SgxResult<sgx_hmac_256bit_tag_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut mac = sgx_hmac_256bit_tag_t::default();
    let ret = unsafe {
        sgx_hmac_sha256_msg(src as * const _ as * const u8,
                            size as i32,
                            key as * const u8,
                            SGX_HMAC256_KEY_SIZE as i32,
                            &mut mac as * mut sgx_hmac_256bit_tag_t as * mut u8,
                            SGX_HMAC256_MAC_SIZE as i32)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(mac),
        _ => Err(ret),
    }
}

pub fn rsgx_hmac_sha256_slice<T>(key: &sgx_hmac_256bit_key_t, src: &[T]) -> SgxResult<sgx_hmac_256bit_tag_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut mac = sgx_hmac_256bit_tag_t::default();
    let ret = unsafe {
        sgx_hmac_sha256_msg(src.as_ptr() as * const u8,
                            size as i32,
                            key as * const u8,
                            SGX_HMAC256_KEY_SIZE as i32,
                            &mut mac as * mut sgx_hmac_256bit_tag_t as * mut u8,
                            SGX_HMAC256_MAC_SIZE as i32)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(mac),
        _ => Err(ret),
    }
}

fn rsgx_hmac256_init(key: &sgx_hmac_256bit_key_t, hmac_handle: &mut sgx_hmac_state_handle_t) -> sgx_status_t {

    unsafe {
        sgx_hmac256_init(key as * const sgx_hmac_256bit_key_t as * const u8,
                         SGX_HMAC256_KEY_SIZE as i32,
                         hmac_handle as * mut sgx_hmac_state_handle_t)
    }
}

fn rsgx_hmac256_update_msg<T>(src: &T, hmac_handle: sgx_hmac_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    unsafe {
        sgx_hmac256_update(src as * const _ as * const u8, size as i32, hmac_handle)
    }
}

fn rsgx_hmac256_update_slice<T>(src: &[T], hmac_handle: sgx_hmac_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(src);
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    unsafe {
        sgx_hmac256_update(src.as_ptr() as * const _ as * const u8, size as i32, hmac_handle)
    }
}

fn rsgx_hmac256_final(hmac_handle: sgx_hmac_state_handle_t, hash: &mut sgx_hmac_256bit_tag_t) -> sgx_status_t {

    unsafe {
        sgx_hmac256_final(hash as * mut sgx_hmac_256bit_tag_t as * mut u8,
                          SGX_HMAC256_MAC_SIZE as i32,
                          hmac_handle)
    }
}

fn rsgx_hmac256_close(hmac_handle: sgx_hmac_state_handle_t) -> sgx_status_t {

    unsafe { sgx_hmac256_close(hmac_handle) }
}

pub struct SgxHmacHandle {
    handle: RefCell<sgx_hmac_state_handle_t>,
    initflag: Cell<bool>,
}

impl SgxHmacHandle {

    pub fn new() -> Self {
        SgxHmacHandle{
            handle: RefCell::new(ptr::null_mut() as sgx_hmac_state_handle_t),
            initflag: Cell::new(false),
            }
    }

    pub fn init(&self, key: &sgx_hmac_256bit_key_t) -> SgxError {

        if self.initflag.get() {
            return Ok(());
        }

        let ret = rsgx_hmac256_init(key, self.handle.borrow_mut().deref_mut());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(true);
                Ok(())
            },
            _ => Err(ret),
        }
    }

    pub fn update_msg<T>(&self, src: &T) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_hmac256_update_msg(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    pub fn update_slice<T>(&self, src: &[T]) -> SgxError
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_hmac256_update_slice(src, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(()),
            _ => Err(ret),
        }
    }

    pub fn get_hash(&self) -> SgxResult<sgx_hmac_256bit_tag_t> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut hash = sgx_hmac_256bit_tag_t::default();
        let ret = rsgx_hmac256_final(*self.handle.borrow(), &mut hash);
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(hash),
            _ => Err(ret),
        }
    }

    pub fn close(&self) -> SgxError {

        if !self.initflag.get() {
            return Ok(());
        }

        let ret = {
            let handle = *self.handle.borrow();
            if handle.is_null() {
                sgx_status_t::SGX_SUCCESS
            } else {
                rsgx_hmac256_close(handle)
            }
        };

        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(false);
                *self.handle.borrow_mut() = ptr::null_mut();
                Ok(())
            },
            _ => Err(ret),
        }
    }
}

impl Default for SgxHmacHandle {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxHmacHandle {
    fn drop(&mut self) {
       let _ = self.close();
    }
}

pub const SGX_AESCTR_CTR_SIZE: size_t = 16;
pub type sgx_aes_ctr_128bit_ctr_t = [uint8_t; SGX_AESCTR_CTR_SIZE];

///
/// rsgx_aes_ctr_encrypt performs a Rijndael AES-CTR encryption operation.
///
/// Only a 128bit key size is supported by this Intel(R) SGX SDK cryptography library.
///
/// # Description
///
/// This function encrypts the input data stream of a variable length according to
/// the CTR mode as specified in [NIST SP 800-38A]. The counter can be thought
/// of as an IV which increments on successive encryption or decryption calls. For
/// a given dataset or data stream, the incremented counter block should be used
/// on successive calls of the encryption process for that given stream. However,
/// for new or different datasets/streams, the same counter should not be reused,
/// instead initialize the counter for the new data set.
///
/// It is recommended that the source, destination and counter data buffers are
/// allocated within the enclave.
///
/// # Parameters
///
/// **key**
///
/// A pointer to key to be used in the AES-CTR encryption operation. The size must be 128 bits.
///
/// **src**
///
/// A pointer to the input data stream to be encrypted.
///
/// **ctr**
///
/// A pointer to the initialization vector to be used in the AES-CTR calculation.
///
/// **ctr_inc_bits**
///
/// Specifies the number of bits in the counter to be incremented.
///
/// **dst**
///
/// A pointer to the output encrypted data buffer. This buffer should be allocated by the calling code.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// The pointer is invalid.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An internal cryptography library failure occurred.
///
pub fn rsgx_aes_ctr_encrypt(key: &sgx_aes_ctr_128bit_key_t,
                            src: &[u8],
                            ctr: &sgx_aes_ctr_128bit_ctr_t,
                            ctr_inc_bits: u32,
                            dst: &mut [u8]) -> SgxError {

    let src_len = src.len();
    if src_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if src_len < 1 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let dst_len = dst.len();
    if dst_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if dst_len < src_len {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        sgx_aes_ctr_encrypt(key as * const sgx_aes_ctr_128bit_key_t,
                            src.as_ptr(),
                            src_len as u32,
                            ctr as * const sgx_aes_ctr_128bit_ctr_t as * const u8,
                            ctr_inc_bits,
                            dst.as_mut_ptr())
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

///
/// rsgx_aes_ctr_decrypt performs a Rijndael AES-CTR decryption operation.
///
/// Only a 128bit key size is supported by this Intel(R) SGX SDK cryptography library.
///
/// # Description
///
/// This function decrypts the input data stream of a variable length according to
/// the CTR mode as specified in [NIST SP 800-38A]. The counter can be thought
/// of as an IV which increments on successive encryption or decryption calls. For
/// a given dataset or data stream, the incremented counter block should be used
/// on successive calls of the decryption process for that given stream. However,
/// for new or different datasets/streams, the same counter should not be reused,
/// instead initialize the counter for the new data set.
///
/// It is recommended that the source, destination and counter data buffers are
/// allocated within the enclave.
///
/// # Parameters
///
/// **key**
///
/// A pointer to key to be used in the AES-CTR encryption operation. The size must be 128 bits.
///
/// **src**
///
/// A pointer to the input data stream to be decrypted.
///
/// **ctr**
///
/// A pointer to the initialization vector to be used in the AES-CTR calculation.
///
/// **ctr_inc_bits**
///
/// Specifies the number of bits in the counter to be incremented.
///
/// **dst**
///
/// A pointer to the output decrypted data buffer. This buffer should be allocated by the calling code.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// The pointer is invalid.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An internal cryptography library failure occurred.
///
pub fn rsgx_aes_ctr_decrypt(key: &sgx_aes_ctr_128bit_key_t,
                            src: &[u8],
                            ctr: &sgx_aes_ctr_128bit_ctr_t,
                            ctr_inc_bits: u32,
                            dst: &mut [u8]) -> SgxError {

    let src_len = src.len();
    if src_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if src_len < 1 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let dst_len = dst.len();
    if dst_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if dst_len < src_len {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        sgx_aes_ctr_decrypt(key as * const sgx_aes_ctr_128bit_key_t,
                            src.as_ptr(),
                            src.len() as u32,
                            ctr as * const sgx_aes_ctr_128bit_ctr_t as * const u8,
                            ctr_inc_bits,
                            dst.as_mut_ptr())
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

fn rsgx_ecc256_open_context(ecc_handle: &mut sgx_ecc_state_handle_t) -> sgx_status_t {

    unsafe { sgx_ecc256_open_context(ecc_handle as * mut _ as * mut sgx_ecc_state_handle_t) }
}

fn rsgx_ecc256_close_context(ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t {

    unsafe { sgx_ecc256_close_context(ecc_handle) }
}

fn rsgx_ecc256_create_key_pair(private: &mut sgx_ec256_private_t,
                               public: &mut sgx_ec256_public_t,
                               ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t {
    unsafe {
        sgx_ecc256_create_key_pair(private as * mut sgx_ec256_private_t,
                                   public as * mut sgx_ec256_public_t,
                                   ecc_handle)
    }
}

fn rsgx_ecc256_check_point(point: &sgx_ec256_public_t, ecc_handle: sgx_ecc_state_handle_t, valid: &mut i32) -> sgx_status_t {

    unsafe { sgx_ecc256_check_point(point as * const sgx_ec256_public_t, ecc_handle, valid as * mut i32) }
}

fn rsgx_ecc256_compute_shared_dhkey(private_b: &sgx_ec256_private_t,
                                    public_ga: &sgx_ec256_public_t,
                                    shared_key: &mut sgx_ec256_dh_shared_t,
                                    ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t {
    unsafe {
        sgx_ecc256_compute_shared_dhkey(private_b as * const _ as * mut sgx_ec256_private_t,
                                        public_ga as * const _ as * mut sgx_ec256_public_t,
                                        shared_key as * mut sgx_ec256_dh_shared_t,
                                        ecc_handle)
    }
}

/* delete (intel sgx sdk 2.0)
fn rsgx_ecc256_compute_shared_dhkey512(private_b: &sgx_ec256_private_t,
                                       public_ga: &sgx_ec256_public_t,
                                       shared_key: &mut sgx_ec256_dh_shared512_t,
                                       ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t {

    unsafe {
        sgx_ecc256_compute_shared_dhkey512(private_b as * const _ as * mut sgx_ec256_private_t,
                                           public_ga as * const _ as * mut sgx_ec256_public_t,
                                           shared_key as * mut sgx_ec256_dh_shared512_t,
                                           ecc_handle)
    }
}
*/

fn rsgx_ecdsa_sign_msg<T>(data: &T,
                          private: &sgx_ec256_private_t,
                          signature: &mut sgx_ec256_signature_t,
                          ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        sgx_ecdsa_sign(data as * const _  as * const u8,
                       size as u32,
                       private as * const _ as * mut sgx_ec256_private_t,
                       signature as * mut sgx_ec256_signature_t,
                       ecc_handle)
    }
}

fn rsgx_ecdsa_sign_slice<T>(data: &[T],
                            private: &sgx_ec256_private_t,
                            signature: &mut sgx_ec256_signature_t,
                            ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(data);
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        sgx_ecdsa_sign(data.as_ptr() as * const _  as * const u8,
                       size as u32,
                       private as * const _ as * mut sgx_ec256_private_t,
                       signature as * mut sgx_ec256_signature_t,
                       ecc_handle)
    }
}

fn rsgx_ecdsa_verify_msg<T>(data: &T,
                            public: &sgx_ec256_public_t,
                            signature: &sgx_ec256_signature_t,
                            result: &mut sgx_generic_ecresult_t,
                            ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {

        let mut verify: u8 = 0;
        let ret = sgx_ecdsa_verify(data as * const _ as * const u8,
                                   size as u32,
                                   public as * const sgx_ec256_public_t,
                                   signature as * const _ as * mut sgx_ec256_signature_t,
                                   &mut verify as * mut u8,
                                   ecc_handle);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                let ecresult = sgx_generic_ecresult_t::from_repr(u32::from(verify));
                *result = ecresult.unwrap_or(sgx_generic_ecresult_t::SGX_EC_INVALID_SIGNATURE);
            },
            _ => { *result = sgx_generic_ecresult_t::SGX_EC_INVALID_SIGNATURE; },
        };
        ret
    }
}

fn rsgx_ecdsa_verify_slice<T>(data: &[T],
                              public: &sgx_ec256_public_t,
                              signature: &sgx_ec256_signature_t,
                              result: &mut sgx_generic_ecresult_t,
                              ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(data);
    if size == 0 {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if size > u32::max_value() as usize {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {

        let mut verify: u8 = 0;
        let ret = sgx_ecdsa_verify(data.as_ptr() as * const _ as * const u8,
                                   size as u32,
                                   public as * const sgx_ec256_public_t,
                                   signature as * const _ as * mut sgx_ec256_signature_t,
                                   &mut verify as * mut u8,
                                   ecc_handle);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                let ecresult = sgx_generic_ecresult_t::from_repr(u32::from(verify));
                *result = ecresult.unwrap_or(sgx_generic_ecresult_t::SGX_EC_INVALID_SIGNATURE);
            },
            _ => { *result = sgx_generic_ecresult_t::SGX_EC_INVALID_SIGNATURE; },
        };
        ret
    }
}

fn rsgx_ecdsa_verify_hash(hash: &sgx_sha256_hash_t,
                          public: &sgx_ec256_public_t,
                          signature: &sgx_ec256_signature_t,
                          result: &mut sgx_generic_ecresult_t,
                          ecc_handle: sgx_ecc_state_handle_t) -> sgx_status_t {

    unsafe {

        let mut verify: u8 = 0;
        let ret = sgx_ecdsa_verify_hash(hash as * const sgx_sha256_hash_t as * const u8,
                                        public as * const sgx_ec256_public_t,
                                        signature as * const _ as * mut sgx_ec256_signature_t,
                                        &mut verify as * mut u8,
                                        ecc_handle);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                let ecresult = sgx_generic_ecresult_t::from_repr(u32::from(verify));
                *result = ecresult.unwrap_or(sgx_generic_ecresult_t::SGX_EC_INVALID_SIGNATURE);
            },
            _ => { *result = sgx_generic_ecresult_t::SGX_EC_INVALID_SIGNATURE; },
        };
        ret
    }
}


///
/// ECC GF(p) context state.
///
/// This is a handle to the ECC GF(p) context state allocated and initialized used to perform
/// elliptic curve cryptosystem standard functions. The algorithm stores the intermediate results
/// of calculations performed using this context.
///
pub struct SgxEccHandle{
    handle: RefCell<sgx_ecc_state_handle_t>,
    initflag: Cell<bool>,
}

impl SgxEccHandle {

    ///
    /// Constructs a new, empty SgxEccHandle.
    ///
    pub fn new() -> Self {
        SgxEccHandle{
            handle: RefCell::new(ptr::null_mut() as sgx_ecc_state_handle_t),
            initflag: Cell::new(false),
            }
    }

    ///
    /// open returns an allocated and initialized context for the elliptic curve cryptosystem
    /// over a prime finite field, GF(p).
    ///
    /// This context must be created prior to calling create_key_pair or compute_shared_dhkey.
    /// When the calling code has completed its set of ECC operations, close should be called to
    /// cleanup and deallocate the ECC context.
    ///
    /// # Description
    ///
    /// open is utilized to allocate and initialize a 256-bit
    /// GF(p) cryptographic system. The caller does not allocate memory for the ECC
    /// state that this function returns. The state is specific to the implementation of
    /// the cryptography library and thus the allocation is performed by the library
    /// itself. If the ECC cryptographic function using this cryptographic system is completed
    /// or any error occurs, close should be called to free the state allocated by this algorithm.
    ///
    /// Public key cryptography successfully allows to solving problems of information
    /// safety by enabling trusted communication over insecure channels. Although
    /// elliptic curves are well studied as a branch of mathematics, an interest to the
    /// cryptographic schemes based on elliptic curves is constantly rising due to the
    /// advantages that the elliptic curve algorithms provide in the wireless communications:
    /// shorter processing time and key length.
    ///
    /// Elliptic curve cryptosystems (ECCs) implement a different way of creating public
    /// keys. As elliptic curve calculation is based on the addition of the rational
    /// points in the (x,y) plane and it is difficult to solve a discrete logarithm from
    /// these points, a higher level of safety is achieved through the cryptographic
    /// schemes that use the elliptic curves. The cryptographic systems that encrypt
    /// messages by using the properties of elliptic curves are hard to attack due to
    /// the extreme complexity of deciphering the private key.
    ///
    /// Using of elliptic curves allows shorter public key length and encourages cryptographers
    /// to create cryptosystems with the same or higher encryption
    /// strength as the RSA or DSA cryptosystems. Because of the relatively short key
    /// length, ECCs do encryption and decryption faster on the hardware that
    /// requires less computation processing volumes.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The ECC context state was not initialized properly due to an internal cryptography library failure.
    ///
    pub fn open(&self) -> SgxError {

        if self.initflag.get() {
            return Ok(());
        }

        let ret = rsgx_ecc256_open_context(self.handle.borrow_mut().deref_mut());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(true);
                Ok(())
            },
            _ => Err(ret),
        }
    }

    ///
    /// create_key_pair generates a private/public key pair on the ECC curve for the given
    /// cryptographic system.
    ///
    /// open must be called to allocate and initialize the ECC context prior to making this call.
    ///
    /// # Description
    ///
    /// This function populates private/public key pair. The calling code allocates
    /// memory for the private and public key pointers to be populated. The function
    /// generates a private key p_private and computes a public key p_public of
    /// the elliptic cryptosystem over a finite field GF(p).
    ///
    /// The private key p_private is a number that lies in the range of [1, n-1]
    /// where n is the order of the elliptic curve base point.
    /// The public key p_public is an elliptic curve point such that p_public =
    /// p_private *G, where G is the base point of the elliptic curve.
    /// The context of the point p_public as an elliptic curve point must be created
    /// by using the function open.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Return value
    ///
    /// **sgx_ec256_private_t**
    ///
    /// The private key which is a number that lies in the range of [1, n-1] where n is the order
    /// of the elliptic curve base point.
    ///
    /// **sgx_ec256_public_t**
    ///
    /// The public key which is an elliptic curve point such that:
    ///
    /// public key = private key * G, where G is the base point of the elliptic curve.
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The ECC state is not initialized.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The key creation process failed due to an internal cryptography library failure.
    ///
    pub fn create_key_pair(&self) -> SgxResult<(sgx_ec256_private_t, sgx_ec256_public_t)> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut private = sgx_ec256_private_t::default();
        let mut public = sgx_ec256_public_t::default();
        let ret = rsgx_ecc256_create_key_pair(&mut private, &mut public, *self.handle.borrow());

        match ret {
            sgx_status_t::SGX_SUCCESS => Ok((private, public)),
            _ => Err(ret),
        }
    }

    ///
    /// check_point checks whether the input point is a valid point on the ECC curve for the given cryptographic system.
    ///
    /// open context must be called to allocate and initialize the ECC context prior to making this call.
    ///
    /// # Description
    ///
    /// check_point validates whether the input point is a valid point on the ECC curve for the given cryptographic system.
    ///
    /// # Parameters
    ///
    /// **point**
    ///
    /// A pointer to the point to perform validity check on.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Return value
    ///
    /// **true**
    ///
    /// The input point is valid
    ///
    /// **false**
    ///
    /// The input point is not valid
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The ECC state is not initialized.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// An internal cryptography library failure occurred.
    ///
    pub fn check_point(&self, point: &sgx_ec256_public_t) -> SgxResult<bool> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut valid: i32 = 0;
        let ret = rsgx_ecc256_check_point(point, *self.handle.borrow(), &mut valid);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                if valid > 0 {
                    Ok(true)
                } else {
                    Ok(false)
                }
            },
            _ => Err(ret),
        }
    }

    ///
    /// compute_shared_dhkey generates a secret key shared between two participants of the cryptosystem.
    ///
    /// # Description
    ///
    /// This function computes the Diffie-Hellman shared key based on the enclave’s
    /// own (local) private key and remote enclave’s public Ga Key.
    ///
    /// The function computes a secret number sharedKey, which is a secret key
    /// shared between two participants of the cryptosystem.
    ///
    /// In cryptography, metasyntactic names such as Alice as Bob are normally used
    /// as examples and in discussions and stand for participant A and participant B.
    ///
    /// Both participants (Alice and Bob) use the cryptosystem for receiving a common
    /// secret point on the elliptic curve called a secret key (sharedKey). To
    /// receive a secret key, participants apply the Diffie-Hellman key-agreement
    /// scheme involving public key exchange. The value of the secret key entirely
    /// depends on participants.
    ///
    /// According to the scheme, Alice and Bob perform the following operations:
    ///
    /// 1. Alice calculates her own public key pubKeyA by using her private key
    /// privKeyA: pubKeyA = privKeyA * G, where G is the base point of the
    /// elliptic curve.
    ///
    /// 2. Alice passes the public key to Bob.
    ///
    /// 3. Bob calculates his own public key pubKeyB by using his private key
    /// privKeyB: pubKeyB = privKeyB * G, where G is a base point of the elliptic curve.
    ///
    /// 4. Bob passes the public key to Alice.
    ///
    /// 5. Alice gets Bob's public key and calculates the secret point shareKeyA. When
    /// calculating, she uses her own private key and Bob's public key and applies the
    /// following formula:
    ///
    /// shareKeyA = privKeyA * pubKeyB = privKeyA * privKeyB * G.
    ///
    /// 6. Bob gets Alice's public key and calculates the secret point shareKeyB. When
    /// calculating, he uses his own private key and Alice's public key and applies the
    /// following formula:
    ///
    /// shareKeyB = privKeyB * pubKeyA = privKeyB * privKeyA * G.
    ///
    /// As the following equation is true privKeyA * privKeyB * G =
    /// privKeyB * privKeyA * G, the result of both calculations is the same,
    /// that is, the equation shareKeyA = shareKeyB is true. The secret point serves as
    /// a secret key.
    ///
    /// Shared secret shareKey is an x-coordinate of the secret point on the elliptic
    /// curve. The elliptic curve domain parameters must be hitherto defined by the
    /// function: open.
    ///
    /// # Parameters
    ///
    /// **private_b**
    ///
    /// A pointer to the local private key.
    ///
    /// **public_ga**
    ///
    /// A pointer to the remote public key.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Return value
    ///
    /// The secret key generated by this function which is a common point on the elliptic curve.
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The ECC state is not initialized.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The key creation process failed due to an internal cryptography library failure.
    ///
    pub fn compute_shared_dhkey(&self, private_b: &sgx_ec256_private_t, public_ga: &sgx_ec256_public_t) -> SgxResult<sgx_ec256_dh_shared_t> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut shared_key = sgx_ec256_dh_shared_t::default();
        let ret = rsgx_ecc256_compute_shared_dhkey(private_b, public_ga, &mut shared_key, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(shared_key),
            _ => Err(ret),
        }
    }

    /* delete (intel sgx sdk 2.0)
    pub fn compute_shared_dhkey512(&self, private_b: &sgx_ec256_private_t, public_ga: &sgx_ec256_public_t) -> SgxResult<sgx_ec256_dh_shared512_t> {

        if self.initflag.get() == false {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut shared_key = sgx_ec256_dh_shared512_t::default();
        let ret = rsgx_ecc256_compute_shared_dhkey512(private_b, public_ga, &mut shared_key, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(shared_key),
            _ => Err(ret),
        }
    }
    */

    ///
    /// ecdsa_sign_msg computes a digital signature with a given private key over an input dataset.
    ///
    /// # Description
    ///
    /// This function computes a digital signature over the input dataset based on the
    /// put private key.
    ///
    /// A message digest is a fixed size number derived from the original message
    // with an applied hash function over the binary code of the message. (SHA256
    /// in this case)
    ///
    /// The signer's private key and the message digest are used to create a signature.
    ///
    /// A digital signature over a message consists of a pair of large numbers, 256-bits
    /// each, which the given function computes.
    ///
    /// The scheme used for computing a digital signature is of the ECDSA scheme, an
    /// elliptic curve of the DSA scheme.
    ///
    /// The keys can be generated and set up by the function: create_key_pair.
    ///
    /// The elliptic curve domain parameters must be created by function: open.
    ///
    /// # Parameters
    ///
    /// **data**
    ///
    /// A pointer to the data to calculate the signature over.
    ///
    /// **private**
    ///
    /// A pointer to the private key to be used in the calculation of the signature.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Return value
    ///
    /// The signature generated by this function.
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The ECC state is not initialized.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The signature generation process failed due to an internal cryptography library failure.
    ///
    pub fn ecdsa_sign_msg<T>(&self, data: &T, private: &sgx_ec256_private_t) -> SgxResult<sgx_ec256_signature_t>
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut signature = sgx_ec256_signature_t::default();
        let ret = rsgx_ecdsa_sign_msg(data, private, &mut signature, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(signature),
            _ => Err(ret),
        }
    }

    ///
    /// ecdsa_sign_slice computes a digital signature with a given private key over an input dataset.
    ///
    pub fn ecdsa_sign_slice<T>(&self, data: &[T], private: &sgx_ec256_private_t) -> SgxResult<sgx_ec256_signature_t>
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut signature = sgx_ec256_signature_t::default();
        let ret = rsgx_ecdsa_sign_slice(data, private, &mut signature, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => Ok(signature),
            _ => Err(ret),
        }
    }

    ///
    /// ecdsa_verify_msg verifies the input digital signature with a given public key over an input dataset.
    ///
    /// # Description
    ///
    /// This function verifies the signature for the given data set based on the input public key.
    ///
    /// A digital signature over a message consists of a pair of large numbers, 256-bits
    /// each, which could be created by function: sgx_ecdsa_sign. The scheme
    /// used for computing a digital signature is of the ECDSA scheme, an elliptic
    /// curve of the DSA scheme.
    ///
    /// The elliptic curve domain parameters must be created by function: open.
    ///
    /// # Parameters
    ///
    /// **data**
    ///
    /// A pointer to the signed dataset to verify.
    ///
    /// **public**
    ///
    /// A pointer to the public key to be used in the calculation of the signature.
    ///
    /// **signature**
    ///
    /// A pointer to the signature to be verified.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Return value
    ///
    /// **true**
    ///
    /// Digital signature is valid.
    ///
    /// **false**
    ///
    /// Digital signature is not valid.
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The pointer is invalid.
    ///
    /// **SGX_ERROR_INVALID_STATE**
    ///
    /// The ECC state is not initialized.
    ///
    /// **SGX_ERROR_OUT_OF_MEMORY**
    ///
    /// Not enough memory is available to complete this operation.
    ///
    /// **SGX_ERROR_UNEXPECTED**
    ///
    /// The verification process failed due to an internal cryptography library failure.
    ///
    pub fn ecdsa_verify_msg<T>(&self,
                               data: &T,
                               public: &sgx_ec256_public_t,
                               signature: &sgx_ec256_signature_t) -> SgxResult<bool>
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut result = sgx_generic_ecresult_t::default();
        let ret = rsgx_ecdsa_verify_msg(data, public, signature, &mut result, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                match result {
                    sgx_generic_ecresult_t::SGX_EC_VALID => Ok(true),
                    _ => Ok(false),
                }
            },
            _ => Err(ret),
        }
    }

    ///
    /// ecdsa_verify_slice verifies the input digital signature with a given public key over an input dataset.
    ///
    pub fn ecdsa_verify_slice<T>(&self,
                                 data: &[T],
                                 public: &sgx_ec256_public_t,
                                 signature: &sgx_ec256_signature_t) -> SgxResult<bool>
        where T: Copy + ContiguousMemory {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut result = sgx_generic_ecresult_t::default();
        let ret = rsgx_ecdsa_verify_slice(data, public, signature, &mut result, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                match result {
                    sgx_generic_ecresult_t::SGX_EC_VALID => Ok(true),
                    _ => Ok(false),
                }
            },
            _ => Err(ret),
        }
    }

    pub fn ecdsa_verify_hash(&self,
                             hash: &sgx_sha256_hash_t,
                             public: &sgx_ec256_public_t,
                             signature: &sgx_ec256_signature_t) -> SgxResult<bool> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let mut result = sgx_generic_ecresult_t::default();
        let ret = rsgx_ecdsa_verify_hash(hash, public, signature, &mut result, *self.handle.borrow());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                match result {
                    sgx_generic_ecresult_t::SGX_EC_VALID => Ok(true),
                    _ => Ok(false),
                }
            },
            _ => Err(ret),
        }
    }

    ///
    /// close cleans up and deallocates the ECC 256 GF(p) state that was allocated in function open.
    ///
    /// # Description
    ///
    /// close is used by calling code to deallocate memory used for storing the ECC 256 GF(p) state used
    /// in ECC cryptographic calculations.
    ///
    /// # Requirements
    ///
    /// Library: libsgx_tcrypto.a
    ///
    /// # Errors
    ///
    /// **SGX_ERROR_INVALID_PARAMETER**
    ///
    /// The input handle is invalid.
    ///
    pub fn close(&self) -> SgxError {

        if !self.initflag.get() {
            return Ok(());
        }

        let ret = {
            let handle = *self.handle.borrow();
            if handle.is_null() {
                sgx_status_t::SGX_SUCCESS
            } else {
                rsgx_ecc256_close_context(handle)
            }
        };

        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.initflag.set(false);
                *self.handle.borrow_mut() = ptr::null_mut();
                Ok(())
            },
            _ => Err(ret),
        }
    }
}

impl Default for SgxEccHandle {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxEccHandle {
    ///
    /// close cleans up and deallocates the ECC 256 GF(p) state that was allocated in function open.
    ///
    fn drop(&mut self) {
        let _ = self.close();
    }
}

///
/// The rsgx_rsa3072_sign_msg computes a digital signature for a given dataset based on RSA 3072 private key.
///
/// # Description
///
/// This function computes a digital signature over the input dataset based on the RSA 3072 private key.
///
/// A message digest is a fixed size number derived from the original message with an applied hash function
/// over the binary code of the message. (SHA256 in this case)
///
/// The signer's private key and the message digest are used to create a signature.
///
/// The scheme used for computing a digital signature is of the RSASSA-PKCS1-v1_5 scheme.
///
/// # Parameters
///
/// **data**
///
/// A pointer to the data to calculate the signature over.
///
/// **key**
///
/// A pointer to the RSA key.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Return value
///
/// The signature generated by this function.
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// The RSA key, data is NULL. Or the data size is 0.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// The signature generation process failed due to an internal cryptography library failure.
///
pub fn rsgx_rsa3072_sign_msg<T>(data: &T, key: &sgx_rsa3072_key_t) -> SgxResult<sgx_rsa3072_signature_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut sign = sgx_rsa3072_signature_t::default();
    let ret = unsafe {
        sgx_rsa3072_sign(data as * const _ as * const u8,
                         size as u32,
                         key as * const sgx_rsa3072_key_t,
                         &mut sign as * mut sgx_rsa3072_signature_t)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(sign),
        _ => Err(ret),
    }
}

///
/// The rsgx_rsa3072_sign_slice computes a digital signature for a given dataset based on RSA 3072 private key.
///
pub fn rsgx_rsa3072_sign_slice<T>(data: &[T], key: &sgx_rsa3072_key_t) -> SgxResult<sgx_rsa3072_signature_t>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(data);
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let mut sign = sgx_rsa3072_signature_t::default();
    let ret = unsafe {
        sgx_rsa3072_sign(data.as_ptr() as * const _ as * const u8,
                         size as u32,
                         key as * const sgx_rsa3072_key_t,
                         &mut sign as * mut sgx_rsa3072_signature_t)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(sign),
        _ => Err(ret),
    }
}

///
/// rsgx_rsa3072_verify_msg verifies the input digital signature for the given data- set based on the RSA 3072 public key.
///
/// # Description
///
/// This function verifies the signature for the given data set based on the input RSA 3072 public key.
///
/// A digital signature over a message is a buffer of 384-bytes, which could be created by function: rsgx_rsa3072_sign.
/// The scheme used for computing a digital signature is of the RSASSA-PKCS1-v1_5 scheme.
///
/// # Parameters
///
/// **data**
///
/// A pointer to the signed dataset to be verified.
///
/// **public**
///
/// A pointer to the public key to be used in the calculation of the signature.
///
/// **signature**
///
/// A pointer to the signature to be verified.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Return value
///
/// **true**
///
/// Digital signature is valid.
///
/// **false**
///
/// Digital signature is not valid.
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// The private key, data is NULL. Or the data size is 0.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// Not enough memory is available to complete this operation.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// The verification process failed due to an internal cryptography library failure.
///
pub fn rsgx_rsa3072_verify_msg<T>(data: &T,
                                  public: &sgx_rsa3072_public_key_t,
                                  signature: &sgx_rsa3072_signature_t) -> SgxResult<bool>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of::<T>();
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    unsafe {

        let mut verify = sgx_rsa_result_t::SGX_RSA_INVALID_SIGNATURE;
        let ret = sgx_rsa3072_verify(data as * const _ as * const u8,
                                     size as u32,
                                     public as * const sgx_rsa3072_public_key_t,
                                     signature as * const sgx_rsa3072_signature_t,
                                     &mut verify as * mut sgx_rsa_result_t);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                match verify {
                    sgx_rsa_result_t::SGX_RSA_VALID => Ok(true),
                    _ => Ok(false),
                }
            },
            _ => Err(ret),
        }
    }
}

///
/// rsgx_rsa3072_verify_slice verifies the input digital signature for the given data- set based on the RSA 3072 public key.
///
pub fn rsgx_rsa3072_verify_slice<T>(data: &[T],
                                    public: &sgx_rsa3072_public_key_t,
                                    signature: &sgx_rsa3072_signature_t) -> SgxResult<bool>
    where T: Copy + ContiguousMemory {

    let size = mem::size_of_val(data);
    if size == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if size > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    unsafe {

        let mut verify = sgx_rsa_result_t::SGX_RSA_INVALID_SIGNATURE;
        let ret = sgx_rsa3072_verify(data.as_ptr() as * const _ as * const u8,
                                     size as u32,
                                     public as * const sgx_rsa3072_public_key_t,
                                     signature as * const sgx_rsa3072_signature_t,
                                     &mut verify as * mut sgx_rsa_result_t);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                match verify {
                    sgx_rsa_result_t::SGX_RSA_VALID => Ok(true),
                    _ => Ok(false),
                }
            },
            _ => Err(ret),
        }
    }
}

#[allow(clippy::many_single_char_names)]
pub fn rsgx_create_rsa_key_pair(n_byte_size: i32,
                                e_byte_size: i32,
                                n: &mut [u8],
                                d: &mut [u8],
                                e: &mut [u8],
                                p: &mut [u8],
                                q: &mut [u8],
                                dmp1: &mut [u8],
                                dmq1: &mut [u8],
                                iqmp: &mut [u8]) -> SgxError {

    if (n_byte_size <= 0) || (e_byte_size <= 0) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (n.is_empty()) || (n.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (d.is_empty()) || (d.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (e.is_empty()) || (e.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (p.is_empty()) || (p.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (q.is_empty()) || (q.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (dmp1.is_empty()) || (dmp1.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (dmq1.is_empty()) || (dmq1.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (iqmp.is_empty()) || (iqmp.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        sgx_create_rsa_key_pair(n_byte_size,
                                e_byte_size,
                                n.as_mut_ptr(),
                                d.as_mut_ptr(),
                                e.as_mut_ptr(),
                                p.as_mut_ptr(),
                                q.as_mut_ptr(),
                                dmp1.as_mut_ptr(),
                                dmq1.as_mut_ptr(),
                                iqmp.as_mut_ptr())
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

fn rsgx_create_rsa_priv_key(mod_size: i32,
                            exp_size: i32,
                            e: &[u8],
                            p: &[u8],
                            q: &[u8],
                            dmp1: &[u8],
                            dmq1: &[u8],
                            iqmp: &[u8],
                            new_pri_key: &mut sgx_rsa_key_t) -> sgx_status_t {

    if (mod_size <= 0) || (exp_size <= 0) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (e.is_empty()) || (e.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (p.is_empty()) || (p.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (q.is_empty()) || (q.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (dmp1.is_empty()) || (dmp1.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (dmq1.is_empty()) || (dmq1.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (iqmp.is_empty()) || (iqmp.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        sgx_create_rsa_priv2_key(mod_size,
                                 exp_size,
                                 e.as_ptr(),
                                 p.as_ptr(),
                                 q.as_ptr(),
                                 dmp1.as_ptr(),
                                 dmq1.as_ptr(),
                                 iqmp.as_ptr(),
                                 new_pri_key as * mut sgx_rsa_key_t)
    }
}

fn rsgx_create_rsa_pub_key(mod_size: i32,
                           exp_size: i32,
                           n: &[u8],
                           e: &[u8],
                           new_pub_key: &mut sgx_rsa_key_t) -> sgx_status_t {

    if (mod_size <= 0) || (exp_size <= 0) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (n.is_empty()) || (n.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if (e.is_empty()) || (e.len() > i32::max_value() as usize) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        sgx_create_rsa_pub1_key(mod_size,
                                exp_size,
                                n.as_ptr(),
                                e.as_ptr(),
                                new_pub_key as * mut sgx_rsa_key_t)
    }
}

fn rsgx_free_rsa_key(rsa_key: sgx_rsa_key_t,
                     key_type: sgx_rsa_key_type_t,
                     mod_size: i32,
                     exp_size: i32) -> sgx_status_t {

    if (mod_size <= 0) || (exp_size <= 0) {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        sgx_free_rsa_key(rsa_key, key_type, mod_size, exp_size)
    }
}

fn rsgx_rsa_priv_decrypt_sha256(rsa_key: sgx_rsa_key_t,
                                out_data: &mut [u8],
                                out_len: &mut usize,
                                in_data: &[u8]) -> sgx_status_t {

    if in_data.is_empty() {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if * out_len != 0 && out_data.len() != * out_len {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        let p_out_data: * mut u8 = if *out_len != 0 {
            out_data.as_mut_ptr()
        } else {
            ptr::null_mut()
        };

        sgx_rsa_priv_decrypt_sha256(rsa_key,
                                    p_out_data,
                                    out_len as * mut usize,
                                    in_data.as_ptr(),
                                    in_data.len())
    }
}

fn rsgx_rsa_pub_encrypt_sha256(rsa_key: sgx_rsa_key_t,
                               out_data: &mut [u8],
                               out_len: &mut usize,
                               in_data: &[u8]) -> sgx_status_t {

    if in_data.is_empty() {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }
    if * out_len != 0 && out_data.len() != * out_len {
        return sgx_status_t::SGX_ERROR_INVALID_PARAMETER;
    }

    unsafe {
        let p_out_data: * mut u8 = if *out_len != 0 {
            out_data.as_mut_ptr()
        } else {
            ptr::null_mut()
        };

        sgx_rsa_pub_encrypt_sha256(rsa_key,
                                   p_out_data,
                                   out_len as * mut usize,
                                   in_data.as_ptr(),
                                   in_data.len())
    }
}

pub struct SgxRsaPrivKey {
    key: RefCell<sgx_rsa_key_t>,
    mod_size: Cell<i32>,
    exp_size: Cell<i32>,
    createflag: Cell<bool>,
}

impl SgxRsaPrivKey {

    pub fn new() -> Self {
        SgxRsaPrivKey {
            key: RefCell::new(ptr::null_mut() as sgx_rsa_key_t),
            mod_size: Cell::new(0),
            exp_size: Cell::new(0),
            createflag: Cell::new(false),
        }
    }

    pub fn create(&self,
                  mod_size: i32,
                  exp_size: i32,
                  e: &[u8],
                  p: &[u8],
                  q: &[u8],
                  dmp1: &[u8],
                  dmq1: &[u8],
                  iqmp: &[u8]) -> SgxError {


        if self.createflag.get() {
            return Ok(());
        }

        let ret = rsgx_create_rsa_priv_key(mod_size,
                                           exp_size,
                                           e,
                                           p,
                                           q,
                                           dmp1,
                                           dmq1,
                                           iqmp,
                                           self.key.borrow_mut().deref_mut());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.mod_size.set(mod_size);
                self.exp_size.set(exp_size);
                self.createflag.set(true);
                Ok(())
            },
            _ => Err(ret),
        }
    }

    pub fn decrypt_sha256(&self,
                          out_data: &mut [u8],
                          out_len: &mut usize,
                          in_data: &[u8]) -> SgxError {

        if !self.createflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_rsa_priv_decrypt_sha256(*self.key.borrow(),
                                               out_data,
                                               out_len,
                                               in_data);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                Ok(())
            },
            _ => Err(ret),
        }
    }

    pub fn free(&self) -> SgxError {

        if !self.createflag.get() {
            return Ok(());
        }

        let ret = {
            let key = *self.key.borrow();
            if key.is_null() {
                sgx_status_t::SGX_SUCCESS
            } else {
                rsgx_free_rsa_key(key,
                                  sgx_rsa_key_type_t::SGX_RSA_PRIVATE_KEY,
                                  self.mod_size.get(),
                                  self.exp_size.get())
            }
        };

        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.createflag.set(false);
                *self.key.borrow_mut() = ptr::null_mut();
                Ok(())
            },
            _ => Err(ret),
        }
    }
}

impl Default for SgxRsaPrivKey {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxRsaPrivKey {
    fn drop(&mut self) {
        let _ = self.free();
    }
}

pub struct SgxRsaPubKey {
    key: RefCell<sgx_rsa_key_t>,
    mod_size: Cell<i32>,
    exp_size: Cell<i32>,
    createflag: Cell<bool>,
}

impl SgxRsaPubKey {

    pub fn new() -> Self {
        SgxRsaPubKey {
            key: RefCell::new(ptr::null_mut() as sgx_rsa_key_t),
            mod_size: Cell::new(0),
            exp_size: Cell::new(0),
            createflag: Cell::new(false),
        }
    }

    pub fn create(&self,
                  mod_size: i32,
                  exp_size: i32,
                  n: &[u8],
                  e: &[u8]) -> SgxError {

        if self.createflag.get() {
            return Ok(());
        }

        let ret = rsgx_create_rsa_pub_key(mod_size,
                                          exp_size,
                                          n,
                                          e,
                                          self.key.borrow_mut().deref_mut());
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.mod_size.set(mod_size);
                self.exp_size.set(exp_size);
                self.createflag.set(true);
                Ok(())
            },
            _ => Err(ret),
        }
    }

    pub fn encrypt_sha256(&self,
                          out_data: &mut [u8],
                          out_len: &mut usize,
                          in_data: &[u8]) -> SgxError {

        if !self.createflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }

        let ret = rsgx_rsa_pub_encrypt_sha256(*self.key.borrow(),
                                              out_data,
                                              out_len,
                                              in_data);
        match ret {
            sgx_status_t::SGX_SUCCESS => {
                Ok(())
            },
            _ => Err(ret),
        }
    }

    pub fn free(&self) -> SgxError {

        if !self.createflag.get() {
            return Ok(());
        }

        let ret = {
            let key = *self.key.borrow();
            if key.is_null() {
                sgx_status_t::SGX_SUCCESS
            } else {
                rsgx_free_rsa_key(key,
                                  sgx_rsa_key_type_t::SGX_RSA_PUBLIC_KEY,
                                  self.mod_size.get(),
                                  self.exp_size.get())
            }
        };

        match ret {
            sgx_status_t::SGX_SUCCESS => {
                self.createflag.set(false);
                *self.key.borrow_mut() = ptr::null_mut();
                Ok(())
            },
            _ => Err(ret),
        }
    }
}

impl Default for SgxRsaPubKey {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxRsaPubKey {
    fn drop(&mut self) {
        let _ = self.free();
    }
}

///
/// rsgx_calculate_ecdsa_priv_key generates an ECDSA private key based on an input random seed.
///
/// # Description
///
/// This function generates an ECDSA private key based on an input random seed.
///
/// # Parameters
///
/// **hash_drg**
///
/// Pointer to the input random seed.
///
/// **sgx_nistp256_r_m1**
///
/// Pointer to the buffer for n-1 where n is order of the ECC group used.
///
/// **out_key**
///
/// Pointer to the generated ECDSA private key.
///
/// # Requirements
///
/// Library: libsgx_tcrypto.a
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// Some of the pointers are NULL, or the input size is 0.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// Unexpected error occurred during the ECDSA private key generation.
///
pub fn rsgx_calculate_ecdsa_priv_key(hash_drg: &[u8],
                                     sgx_nistp256_r_m1: &[u8],
                                     out_key: &mut [u8]) -> SgxError {

    if (hash_drg.is_empty()) || (hash_drg.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (sgx_nistp256_r_m1.is_empty()) || (sgx_nistp256_r_m1.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if (out_key.is_empty()) || (out_key.len() > i32::max_value() as usize) {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        sgx_calculate_ecdsa_priv_key(hash_drg.as_ptr(),
                                     hash_drg.len() as i32,
                                     sgx_nistp256_r_m1.as_ptr(),
                                     sgx_nistp256_r_m1.len() as i32,
                                     out_key.as_mut_ptr(),
                                     out_key.len() as i32)
    };

    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

pub fn rsgx_ecc256_calculate_pub_from_priv(priv_key: &sgx_ec256_private_t,
                                           pub_key: &mut sgx_ec256_public_t) -> SgxError {

    let ret = unsafe {
        sgx_ecc256_calculate_pub_from_priv(priv_key as * const sgx_ec256_private_t,
                                           pub_key as * mut sgx_ec256_public_t)
    };

    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

fn rsgx_aes_gcm128_enc_init(key: &sgx_aes_gcm_128bit_key_t,
                            iv: &[u8],
                            aad: &[u8],
                            aes_gcm_state: &mut sgx_aes_state_handle_t) -> SgxError {

    let iv_len = iv.len();
    if iv_len != SGX_AESGCM_IV_SIZE {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let aad_len = aad.len();
    if aad_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        let mut p_aad = if !aad.is_empty() { aad.as_ptr() } else { ptr::null() };
        sgx_aes_gcm128_enc_init(key as * const sgx_aes_gcm_128bit_key_t as * const u8,
                                iv.as_ptr(),
                                iv_len as u32,
                                p_aad,
                                aad_len as u32,
                                aes_gcm_state as * mut sgx_aes_state_handle_t)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

fn rsgx_aes_gcm128_enc_update(src: &[u8],
                              dst: &mut [u8],
                              aes_gcm_state: sgx_aes_state_handle_t) -> SgxError {

    let src_len = src.len();
    if src_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if src_len == 0 {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    let dst_len = dst.len();
    if dst_len > u32::max_value() as usize {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }
    if dst_len == 0 || dst_len < src_len {
        return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
    }

    let ret = unsafe {
        sgx_aes_gcm128_enc_update(src.as_ptr(),
                                  src_len as u32,
                                  dst.as_mut_ptr(),
                                  aes_gcm_state)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

fn rsgx_aes_gcm128_enc_get_mac(aes_gcm_state: sgx_aes_state_handle_t) -> SgxResult<sgx_aes_gcm_128bit_tag_t> {

    let mut mac = sgx_aes_gcm_128bit_tag_t::default();
    let ret = unsafe {
        sgx_aes_gcm128_enc_get_mac(&mut mac as * mut sgx_aes_gcm_128bit_tag_t as * mut u8 , aes_gcm_state)
    };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(mac),
        _ => Err(ret),
    }
}

fn rsgx_aes_gcm_close(aes_gcm_state: sgx_aes_state_handle_t) -> SgxError {

    let ret = unsafe { sgx_aes_gcm_close(aes_gcm_state) };
    match ret {
        sgx_status_t::SGX_SUCCESS => Ok(()),
        _ => Err(ret),
    }
}

pub struct SgxAesHandle {
    handle: RefCell<sgx_aes_state_handle_t>,
    initflag: Cell<bool>,
}

impl SgxAesHandle {

    pub fn new() -> Self {
        SgxAesHandle{
            handle: RefCell::new(ptr::null_mut() as sgx_aes_state_handle_t),
            initflag: Cell::new(false),
            }
    }

    pub fn init(&self, key: &sgx_aes_gcm_128bit_key_t, iv: &[u8], aad: &[u8]) -> SgxError {

        if self.initflag.get() {
            return Ok(());
        }
        rsgx_aes_gcm128_enc_init(key, iv, aad, self.handle.borrow_mut().deref_mut())
    }

    pub fn update(&self, src: &[u8], dst: &mut [u8]) -> SgxError {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }
        rsgx_aes_gcm128_enc_update(src, dst, *self.handle.borrow())
    }

    pub fn get_mac(&self) -> SgxResult<sgx_aes_gcm_128bit_tag_t> {

        if !self.initflag.get() {
            return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
        }
        rsgx_aes_gcm128_enc_get_mac(*self.handle.borrow())
    }

    pub fn close(&self) -> SgxError {

        if !self.initflag.get() {
            return Ok(());
        }

        let ret = {
            let handle = *self.handle.borrow();
            if handle.is_null() {
                Ok(())
            } else {
                rsgx_aes_gcm_close(handle)
            }
        };

        if ret.is_ok() {
            self.initflag.set(false);
            *self.handle.borrow_mut() = ptr::null_mut();
        }
        ret
    }
}

impl Default for SgxAesHandle {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for SgxAesHandle {
    fn drop(&mut self) {
       let _ = self.close();
    }
}
