/*******************************************************************************
 * (c) Copyright 2012-2014 Microsemi SoC Products Group.  All rights reserved.
 *
 * PUF system service example.
 *
 * Please refer to file README.TXT for further details about this example.
 *
 * SVN $Revision: 6180 $
 * SVN $Date: 2014-03-05 11:22:36 +0530 (Wed, 05 Mar 2014) $
 */
#include <string.h>
#include "drivers/mss_sys_services/mss_sys_services.h"
#include "drivers/mss_uart/mss_uart.h"
#include "helper.h"
#include "drivers/mss_nvm/mss_nvm.h"

#define PUF_USER_KC_ERROR_MSG_LENGTH        18
#define PUF_USER_AC_ERROR_MSG_LENGTH        9
#define USER_PUF_KEY_ERROR_MSG_LENGTH       7
#define PUF_SEED_ERROR_MSG_LENGTH           5
#define MAX_KEY_NUMBER                      57u
#define MAX_USER_KEY_SIZE                   128u
#define EXPORTED_KEY_CODES_MAX_SIZE         2000u


/*==============================================================================
  Messages displayed over the UART.
  - Press \"c\" to hide user key.\r\n\
  - Press \"a\" to fetch PUF ECC public key.\r\n\
 */
const uint8_t g_greeting_msg[] =
"\r\n\r\n\
**********************************************************************\r\n\
***************** SmartFusion2 PUF Services Example ******************\r\n\
**********************************************************************\r\n\
 This example project exercises PUF service\r\n\
  - Press \"1\" to create user activation code.\r\n\
  - Press \"2\" to delete user activation code.\r\n\
  - Press \"3\" to read number of key code.\r\n\
  - Press \"4\" to enroll intrinsic key.\r\n\
  - Press \"5\" to enroll extrinsic key.\r\n\
  - Press \"6\" to export all keys code.\r\n\
  - Press \"7\" to import all keys code.\r\n\
  - Press \"8\" to delete keys code.\r\n\
  - Press \"9\" to retrieve user key.\r\n\
  - Press \"b\" to get random seed.\r\n\
----------------------------------------------------------------------\r\n";
const uint8_t g_separator[] = 
"\r\n----------------------------------------------------------------------\r\n";

/*==============================================================================
  Command line interface defines.
 */
#define INVALID_USER_INPUT      -1
#define MAX_KEY_CODE_SIZE       524u
#define INTRINSIC_KEY           1u
#define EXTRINSIC_KEY           0u
#define ASCII_KEY_SIZE_LENGTH   4
#define ASCII_KEY_LENGTH        128


/*==============================================================================
 * Global Variables.
 */
uint16_t enroll_key_size[57] = {0x00};
uint8_t enroll_key_addr[57][4] = {{0x00}};

/*------------------------------------------------------------------------------
 * The g_my_user_key[] buffer will be used to contain the user guide when its
 * value is needed. The key value is loaded into this buffer by calling
 * retrieve_my_user_key()
 */
volatile uint8_t g_my_user_key[256] = {0x00};
uint8_t* p_my_user_key = (uint8_t*)&g_my_user_key;

/*------------------------------------------------------------------------------
 * The g_export_key_codes[] buffer will be used to store encrypted key code and 
 * user activation code.
 */
uint8_t g_export_key_codes[EXPORTED_KEY_CODES_MAX_SIZE];

/*------------------------------------------------------------------------------
 * The g_import_key_codes[] buffer will be used to retrive already exported 
 * encrypted key code and user activation code.
 */
uint8_t g_import_key_codes[EXPORTED_KEY_CODES_MAX_SIZE];

/*==============================================================================
  Private functions.
*/
static void display_greeting(void);
static void get_number_of_keys(void);
static void enroll_my_keys(uint8_t key_type);
static void retrieve_exported_key_codes(void);
static void import_key_codes(void);
static void delete_kc(void);
///static void display_encrypt_key_code(uint8_t* user_key_code, uint8_t no_of_keys);
static uint8_t get_key_number(void);
static void retrieve_my_user_key(void);
static void read_puf_ecc_public_key(void);
static void get_random_seed(void);
//static uint16_t get_key_size(void);
static uint16_t get_key(uint8_t* rec_key, uint16_t key_size);
static void display_import_key_code(uint8_t* import_key, uint8_t no_of_keys);
//static void hide_my_user_key(void);

/*==============================================================================
  UART selection.
  Replace the line below with this one if you want to use UART1 instead of
  UART0:
  mss_uart_instance_t * const gp_my_uart = &g_mss_uart0;
 */
mss_uart_instance_t * const gp_my_uart = &g_mss_uart1;

/*==============================================================================
  Main function.
 */
int main()
{
    uint8_t status;
    size_t rx_size;
    uint8_t rx_buff[1];
    
    SYSREG->WDOG_CR = 0x0000;        /*Disable Watch-dog*/
    
    MSS_SYS_init(MSS_SYS_NO_EVENT_HANDLER);
    
    MSS_UART_init(gp_my_uart,
                  MSS_UART_115200_BAUD,
                  MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT);
    
    /* Display greeting message. */
    display_greeting();
    
    for(;;)
    {
        /* Start command line interface if any key is pressed. */
        rx_size = MSS_UART_get_rx(gp_my_uart, rx_buff, sizeof(rx_buff));
        if(rx_size > 0)
        {
            switch(rx_buff[0])
            {
                case '1':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected create user activation code service.");
                    /*Create activation code*/
                    status = MSS_SYS_puf_create_activation_code();
                    if(MSS_SYS_SUCCESS == status)
                    {
                        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service create user activation code successful.");
                    }
                    else
                    {
                        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service create user activation code fail.");
                        display_user_ac_error(status);
                    }
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '2':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected delete user activation code service.");
                    /*Delete activation code*/
                    status = MSS_SYS_puf_delete_activation_code();
                    if(MSS_SYS_SUCCESS == status)
                    {
                        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service delete user activation code successful.");
                    }
                    else
                    {
                        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service delete user activation code fail.");
                        display_user_ac_error(status);
                    }
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '3':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected get number of keys service.");
                    
                    /* Read the number of user keys */
                    get_number_of_keys();
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '4':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected create user key using intrinsic key service.");
                    
                    /* Create new user key code from the intrinsic key */
                    enroll_my_keys(INTRINSIC_KEY);
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '5':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected create user key using extrinsic key service.");
                    
                    /* Create new user key code from the extrinsic key */
                    enroll_my_keys(EXTRINSIC_KEY);
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '6':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected export all key code service.");
                    
                    /* Export All key codes */
                    retrieve_exported_key_codes();
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '7':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected import all key code service.");
                    
                    /* Import All key codes */
                    import_key_codes();
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '8':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected delete key code service.");
                    /* Delete key code */
                    delete_kc();
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                
                case '9':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected fetch user PUF key service.");
                    /*fetch user PUF key*/
                    retrieve_my_user_key();
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;
                

                case 'b':
                    MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)" Selected get PUF seed service.");
                    
                    /* Read PUF seed */
                    get_random_seed();
                    MSS_UART_polled_tx_string(gp_my_uart, g_separator);
                break;

                default:
                	MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t *)" Error: Invalid action.");
                    display_greeting();
                break;
            }
        }
    }
}

/*==============================================================================
  Get the numbers of keys.
 */
static void get_number_of_keys(void)
{
    uint8_t status = 0u;
    uint8_t key_numbers = 0u;
    
    status = MSS_SYS_puf_get_number_of_keys(&key_numbers);
    if(MSS_SYS_SUCCESS == status)
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service get number of user key code successful.\r\n Number of user key code:");
        display_hex_values(&key_numbers, 1);
    }
    else
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service get number of user key code fail.");
        display_puf_user_kc_error(status);
    }
}

/*==============================================================================
  Enroll a user key with the SRAM-PUF. The user key will not be visible in
  RAM but will instead be stored in the SRAM-PUF hardware block.
 */
static void enroll_my_keys(uint8_t key_type)
{
    uint8_t status = 0u;
    uint8_t key_number = 0u;
    uint16_t key_size;

    uint8_t user_ext_key_addr[MAX_USER_KEY_SIZE] = {0x00};
    
    status = MSS_SYS_puf_get_number_of_keys(&key_number);
    if(MSS_SYS_SUCCESS == status)
    {
        if(key_number <= MAX_KEY_NUMBER)
        {
            /* Get the intrinsic/extrinsic key size from UART terminal. */
          //  key_size = get_key_size();
        	key_size = 8;
                   
            if(key_type == 1)
            {          
                /* Enroll intrinsic key. */
                status = MSS_SYS_puf_enroll_key(key_number, key_size / 8, 0u, p_my_user_key);
            }
            else
            {
                /* Read the key from UART terminal. */
                get_key(&user_ext_key_addr[0], key_size);
                
                /* Enroll extrinsic key */
                status = MSS_SYS_puf_enroll_key(key_number, key_size / 8, &user_ext_key_addr[0], p_my_user_key);
            }
        
            if(MSS_SYS_SUCCESS == status)
            {            
                /* Print the message on UART terminal. */
                MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service create key code successful.");
                
                /* Store key size. */
                enroll_key_size[key_number] = key_size;
            
                /* Store Key address. */
                write_ptr_value_into_array(p_my_user_key, &enroll_key_addr[key_number][0], 0);
            
                p_my_user_key = p_my_user_key + enroll_key_size[key_number];
            }
            else
            {
                MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service create key code fail.");
                display_puf_user_kc_error(status);
            }
        }
        else
        {
            MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Enrolled number of keys exceeded.");
        }
    }
    else
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Failed to read number of keys enrolled.");
    }
}

/*==============================================================================
  Function to export all user key codes.
 */
static void retrieve_exported_key_codes(void)
{
    uint8_t status = 0u;

    nvm_status_t write_status;

    
    clear_variable(g_export_key_codes, EXPORTED_KEY_CODES_MAX_SIZE);
    
    /* Export all key codes */
		status = MSS_SYS_puf_export_keycodes(&g_export_key_codes[0]);
    
    if(MSS_SYS_SUCCESS == status)
    {

    	write_status = NVM_write(0xfa00, g_export_key_codes, sizeof(g_export_key_codes), NVM_DO_NOT_LOCK_PAGE);
    	MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service export all key code successful.");

    }
    else
    {
        /* Display error message along with error information on terminal window. */
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service export all key code fail.");
        display_puf_user_kc_error(status);
    }
}

/*==============================================================================
  Function to import all user key codes.
 */
static void import_key_codes(void)
{
    uint8_t status;
    uint8_t key_numbers;
    nvm_status_t read_status;

    /* Import all key codes */

	read_status =  MSS_NVM_read( 0xfa00, g_import_key_codes, sizeof(g_import_key_codes));
	status = MSS_SYS_puf_import_keycodes (&g_import_key_codes[0]);

    if(MSS_SYS_SUCCESS == status)
    {

        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service import all Keys code successful.");
        
        /* Get the number of keys */
        status = MSS_SYS_puf_get_number_of_keys(&key_numbers);
        display_import_key_code(&g_import_key_codes[0], key_numbers);
    }
    else
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service import all keys code fail.");
        display_puf_user_kc_error(status);
    }
}

/*==============================================================================
  Function to delete user key codes.
 */
static void delete_kc(void)
{
    uint8_t status;
    uint8_t key_number = 0u;
    
    /* Get the key number to be deleted from the user. */
    key_number = get_key_number();
    
    /* Delete key codes */
    status = MSS_SYS_puf_delete_key(key_number);
    if(MSS_SYS_SUCCESS == status)
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\n\r Service delete KC successful.");
    }
    else
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\n\r Service delete KC fail.");
        display_puf_user_kc_error(status);
    }
}

/*==============================================================================
  Function to retrieve the user PUF key.
 */
static void retrieve_my_user_key(void)
{
    uint8_t key_number = 0u;
    uint8_t status = 0u;
    uint8_t* p_retrive_my_user_key;
   // uint16_t key_size = 0u;
    
    /* Read the key number from terminal window. */
    key_number = get_key_number();
    /*key_size = get_key_size();
    enroll_key_size[key_number] =key_size;*/

        
    /* Send the command through COMBLK. */
    status = MSS_SYS_puf_fetch_key(key_number, &p_retrive_my_user_key);

  
    if(MSS_SYS_SUCCESS == status)
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service fetch user PUF key successful.");
        
        /* Display the key information on Hyper terminal. */
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Key Number:");
        display_hex_values(&key_number, 1);
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Key Code:\n\r");
        display_hex_values(p_retrive_my_user_key, 8);

    }
    else
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Service fetch user PUF key fail.");
        display_read_user_puf_key_error(status);
    }
}


/*==============================================================================
  Function to get the PUF seed.
 */
static void get_random_seed(void)
{
    uint8_t status = 0u;
    uint8_t puf_seed[32];
    
    status = MSS_SYS_puf_get_random_seed(&puf_seed[0]);
    if(MSS_SYS_SUCCESS == status)
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\n\r Service get PUF seed successful. \n\n\r PUF seed values:");
        display_hex_values(puf_seed, 32);
    }
    else
    {
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\n\r Service get PUF seed fail.");
        display_puf_seed_error(status);
    }
}



/*==============================================================================
  Function to read key number from terminal.
 */
static uint8_t get_key_number(void)
{
    uint8_t key_number[2] = {0x00u};
    uint16_t count = 0u;
    const uint8_t key_msg[] = "\r\n Enter key number in hex format:";
    
    MSS_UART_polled_tx_string(gp_my_uart, key_msg);
    
    /* Read the key number from UART terminal to delete the key code.*/
    count = get_data_from_uart(&key_number[0], 2, key_msg);
    
    /* Convert ascii data to hex format. */
    convert_ascii_to_hex(&key_number[0], 2);
    
    if(count == 1)
    {
        key_number[0] = key_number[0] >> 4;
    }
    
    return key_number[0];
}

/*==============================================================================
  Function to read the key from UART terminal.
 */
static uint16_t get_key(uint8_t* rec_key, uint16_t key_size)
{
    uint8_t read_user_key[MAX_USER_KEY_SIZE * 2] = {0x00};
    uint16_t count;
    const uint8_t key_msg[] = "\r\n Enter key in hex format: \n\r";
    
    clear_variable(rec_key, MAX_USER_KEY_SIZE);
    
    /* Display message on UART terminal asking for key. */
    MSS_UART_polled_tx_string(gp_my_uart, key_msg);
    
    /* Read the data from UART terminal. */
    count = get_data_from_uart(&read_user_key[0], (key_size * 2), key_msg);
    //count = get_data_from_uart(&rec_key[0], (key_size * 2), key_msg);
       
    /* Convert ascii data to hex format. */
    convert_ascii_to_hex(read_user_key, (key_size * 2));
    
    memcpy(rec_key, &read_user_key[0], key_size);
        
    return count / 2;
}

/*==============================================================================
  Display greeting message when application is started.
 */
static void display_greeting(void)
{
    MSS_UART_polled_tx_string(gp_my_uart, g_greeting_msg);
}

/*==============================================================================
  Function to display imported key code.
 */
static void display_import_key_code(uint8_t* import_key, uint8_t no_of_keys)
{
    uint8_t* key_address;
    uint8_t arr_index;
    uint8_t key_no;
    
    for(key_no = 2; key_no < no_of_keys; key_no++)
    {
        /* Read the key address */
        arr_index = (key_no - 2) * 4;
        
        write_array_into_ptr_value(&key_address, import_key, arr_index);
        
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Key Number:");
        display_hex_values(&key_no, 1);
        MSS_UART_polled_tx_string(gp_my_uart, (const uint8_t*)"\r\n Key Value:\n\r");
       display_hex_values(key_address, 8);
       // display_hex_values(import_key, enroll_key_size[key_no]);
    }
}

