#ifndef CRYPTO_MODULE_H
#define CRYPTO_MODULE_H

#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/aes.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#ifndef USE_SCRYPTO
#include <openssl/modes/modes_lcl.h>
#else
#include <openssl/evp.h>
#endif

#include "tz_debug.h"

#ifdef USE_SCRYPTO
BIGNUM *EC_POINT_point2bn(const EC_GROUP *, const EC_POINT *,
	point_conversion_form_t form, BIGNUM *, BN_CTX *);
EC_POINT *EC_POINT_bn2point(const EC_GROUP *, const BIGNUM *,
	EC_POINT *, BN_CTX *);
#endif

typedef uint16_t CRYPTO_STATUS;

// CRYPTO Return status
#define CRYPTO_STATUS_SUCCESS                   0x0000
#define CRYPTO_STATUS_FAILED                    0x0001
#define CRYPTO_STATUS_INVALID_ARGUMENT          0x0002
#define CRYPTO_STATUS_OTP_KEY_CORRUPTED         0x0003

/* All sizes are in bytes */
#define AP_ID_SIZE                              16

#define AES_BLOCK_SIZE                          16
#define AES_128_KEY_SIZE                        16
#define AES_256_KEY_SIZE                        32

#define ECC_PRIVKEY_SIZE                        32
#define ECC_PUBKEY_SIZE                         65
#define ECC_SECRET_SIZE                         32

#define ECDSA_SIG_SIZE                          64
#define ECDSA_SIG_WITH_ASN1_SIZE                72

#define RSA_KEY_COMPONENT_SIZE                  256
#define RSA_SIG_SIZE                            256
#define RSA_SIG_WITH_ASN1_SIZE                  277

#define SHA1_DIGEST_SIZE                        20
#define SHA256_DIGEST_SIZE                      32

#define ENCRYPTED_OTP_KEY_SIZE                  40

#define MD_TYPE_NONE                            0
#define MD_TYPE_SHA1                            1
#define MD_TYPE_SHA256                          2

#define WRAPPED_CERT_SIZE                       3500

/* ============================= Chip dependency cryptography ============================= */

CRYPTO_STATUS crypto_gen_random( uint8_t *out, uint32_t size );
CRYPTO_STATUS crypto_get_tz_encryption_key( uint8_t out[AES_256_KEY_SIZE] );
CRYPTO_STATUS crypto_get_apId( uint8_t apId[AP_ID_SIZE] );

/* =============================== Elliptic curve cryptography =============================== */

typedef struct ecc_key_st {
    void    *ecc_keypair;
    uint8_t pubkey_binary[ECC_PUBKEY_SIZE];
    uint32_t pubkey_size;
    uint8_t  secretkey[ECC_SECRET_SIZE];
} ecc_key_t;

/* Generate ephimeral elliptic keypair */
CRYPTO_STATUS crypto_gen_ecc_key( ecc_key_t *ecc_key );

/* Perform elliptic Diffie-Hellman algorithm to derrive key from ephimeral private key and peer's public key */
CRYPTO_STATUS crypto_gen_ecc_secret( ecc_key_t *ecc_key, const uint8_t *peer_key, uint32_t peer_key_size );

/* Free resourses at pointers of structure containing elliptic key */
void crypto_clear_ecc_context( ecc_key_t *ecc_key );

/* Re-generate ephimeral elliptic public key */
CRYPTO_STATUS crypto_regen_ecc_pubkey(ecc_key_t *ecc_key, uint8_t *pub_key, uint32_t pub_key_size);
CRYPTO_STATUS crypto_regen_ecc_privkey(ecc_key_t *ecc_key, uint8_t *priv_key, uint32_t priv_key_size);


/* ECDSA sign and verify */
CRYPTO_STATUS crypto_ecdsa_sign_with_sha256(void *eckey, uint8_t *in, uint32_t inLen,
    uint8_t *sig_r, int32_t *sig_r_Len, uint8_t *sig_s, int32_t *sig_s_Len);

CRYPTO_STATUS crypto_ecdsa_verify_with_sha256(void *eckey, uint8_t *in, uint32_t inLen,
    uint8_t *sig_r, int32_t sig_r_Len, uint8_t *sig_s, int32_t sig_s_Len);

/* =============================== RSA cryptography (for tests) =============================== */
typedef struct rsa_key_st {
    uint8_t modulus[300];
    uint32_t modulus_size;
    uint8_t publicExponent[5];
    uint32_t publicExponent_size;
    uint8_t privateExponent[300];
    uint32_t privateExponent_size;
    uint8_t prime1[150];
    uint32_t prime1_size;
    uint8_t prime2[150];
    uint32_t prime2_size;
    uint8_t exponent1[150];
    uint32_t exponent1_size;
    uint8_t exponent2[150];
    uint32_t exponent2_size;
    uint8_t coefficient[150];
    uint32_t coefficient_size;
} rsa_key_t;

/* Generate rsa keypair */
CRYPTO_STATUS crypto_gen_rsa_key(RSA **rsa_key, rsa_key_t *rsa_key_info);

/* Re-generate rsa key */
CRYPTO_STATUS crypto_regen_rsa_pubkey(RSA **rsa_key, rsa_key_t *rsa_key_info);
CRYPTO_STATUS crypto_regen_rsa_privkey(RSA **rsa_key, rsa_key_t *rsa_key_info);

/* Free resourses at pointers of structure containing rsa key */
void crypto_clear_rsa_key(RSA **rsa_key);

/* RSA  encrypt / decrypt with OAEP padding */
CRYPTO_STATUS crypto_rsa_oaep_encrypt(RSA *rsakey, const uint8_t *in, uint32_t in_size, uint8_t *out, uint32_t out_size);
CRYPTO_STATUS crypto_rsa_oaep_decrypt(RSA *rsakey, const uint8_t *in, uint32_t in_size, uint8_t *out, uint32_t *out_size);

#if 0
/* RSA  PKCS sign and verify */
CRYPTO_STATUS crypto_rsa_sign_pkcs(uint8_t md_type, uint8_t isHashed, RSA *rsakey, uint8_t *data, uint32_t data_len, uint8_t *sig, uint32_t *sig_len);
CRYPTO_STATUS crypto_rsa_verify_pkcs(uint8_t md_type, uint8_t isHashed, RSA *rsakey, uint8_t *data, uint32_t data_len, uint8_t *sig, uint32_t sig_len);
#endif

/* RSA  PSS sign and verify */
CRYPTO_STATUS crypto_rsa_sign_pss(uint8_t md_type, uint8_t isHashed, RSA *rsakey, uint8_t *data, uint32_t data_len, uint8_t *sig, uint32_t *sig_len);
CRYPTO_STATUS crypto_rsa_verify_pss(uint8_t md_type, uint8_t isHashed, RSA *rsakey, uint8_t *data, uint32_t data_len, uint8_t *sig, uint32_t sig_len);

#if 0
int crypto_rsa_verify_pkcs_rawkey(unsigned char *data, unsigned int data_len, unsigned char *sig, unsigned int sig_len,
                                    const uint8_t *rsa_key_N, uint32_t rsa_key_N_size,
                                    const uint8_t *rsa_key_e, uint32_t rsa_key_e_size);
#endif

/* =============================== AES-256 in CBC mode =============================== */

/* Encrypt buffer with AES-256 CBC. Assumes input is already padded */
CRYPTO_STATUS crypto_aes_cbc_256_encrypt( const uint8_t *in, uint32_t size, uint8_t *out, uint32_t *outSize, 
                                       const uint8_t iv[AES_BLOCK_SIZE], const uint8_t key[AES_256_KEY_SIZE] );

/* Decrypt buffer with AES-256 CBC. Does not remove the padding */
CRYPTO_STATUS crypto_aes_cbc_256_decrypt(  const uint8_t *in, uint32_t size, uint8_t *out, uint32_t *outSize, 
                                       const uint8_t iv[AES_BLOCK_SIZE], const uint8_t key[AES_256_KEY_SIZE] );

/* Remove PKCS#5 padding from decrypted data (function just returns actual data size) */
uint32_t crypto_aes_256_unpadded_data_size( const uint8_t *data, uint32_t size );

/* ======================= AES-256 in "key wrap" mode for encryption of OTP ======================= */

CRYPTO_STATUS crypto_aes_256_wrap_otp( const uint8_t input[AES_256_KEY_SIZE],
                                   uint8_t output[ENCRYPTED_OTP_KEY_SIZE],
                                   const uint8_t key[AES_256_KEY_SIZE] );

CRYPTO_STATUS crypto_aes_256_unwrap_otp( const uint8_t input[ENCRYPTED_OTP_KEY_SIZE],
                                     uint8_t output[AES_256_KEY_SIZE],
                                     const uint8_t key[AES_256_KEY_SIZE] );

/* =============================== Other crypto functions =============================== */

/* Encrypt single block with AES-256 */
CRYPTO_STATUS crypto_aes_256_block_encrypt( const uint8_t in[AES_BLOCK_SIZE],
                                         uint8_t out[AES_BLOCK_SIZE],
                                         const uint8_t key[AES_256_KEY_SIZE] );

/* Additional arguments are pairs { uint8_t*, uint32_t } - blocks of data to be hashed
 * Last argument is a NULL pointer */
CRYPTO_STATUS crypto_sha1( uint8_t out[SHA1_DIGEST_SIZE], ... );

/* Additional arguments are pairs { uint8_t*, uint32_t } - blocks of data to be hashed
 * Last argument is a NULL pointer */
CRYPTO_STATUS crypto_sha256( uint8_t out[SHA256_DIGEST_SIZE], ... );

/* Same syntax as in function above, but with CMAC key added */
CRYPTO_STATUS crypto_aes_256_cmac( uint8_t out[AES_BLOCK_SIZE], const uint8_t key[AES_256_KEY_SIZE], ... );

/* Additional arguments are pairs { uint8_t*, uint32_t } - blocks of data to be hmaced with MD_TYPE_SHA1,MD_TYPE_SHA256, MD_TYPE_SHA384
 * Last argument is a NULL pointer */
CRYPTO_STATUS crypto_hmac (uint8_t md_type, uint8_t *out, const uint8_t *key, uint32_t keyLen, ...);

/* Comapare AES CMACs in a constant-time way */
uint32_t crypto_secure_mac_cmp( const uint8_t mac1[AES_BLOCK_SIZE], const uint8_t mac2[AES_BLOCK_SIZE] );

/* Fill memory with zeros to wipe sensetive data */
void crypto_clear_mem( void *mem, uint8_t size_bytes );


#endif /* CRYPTO_MODULE_H */
