/**
  ******************************************************************************
  * @file           : provisioning.c
  * @brief          : Provisioning management functions
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */


/* Includes ------------------------------------------------------------------*/
#include "provisioning.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "flash_mng.h"
#include "logger.h"
#include "app_aliro.h"
#include "rfal_platform.h"

#include "provisioning_info.h"
#ifdef ALIRO_TRUST_OVERRIDE
/* Overrides the READER_*_2 / CREDENTIAL_ISSUER_PUB_KEY_2 / READER_ID_2 macros
 * that provisioning_info.h -> User_provisioning.h just pulled in with the
 * ones aliro-trustgen emits from our ~/aliro-trust bundle. Must come AFTER
 * provisioning_info.h so the #undef / #define dance actually wins. */
#include "aliro_trust.h"
#endif
#include "sm_crypto.h"

/* Constants --------------------------------------------------------*/
/**
 * @def AUTH_CERTIFICATE_MAX_LEN_BYTES
 * @brief Maximum allowed length (in bytes) for an authentication certificate.
 *
 * This macro defines the upper limit for the size of authentication certificates
 * used in the provisioning process. The value is set to 420 bytes.
 */
#define AUTH_CERTIFICATE_MAX_LEN_BYTES ((uint32_t)420)

/**
 * @def PROVISIONING_JSON_INVALIDATE_STR
 * @brief String literal used to represent the "invalidate" action in provisioning JSON operations.
 */
#define PROVISIONING_JSON_INVALIDATE_STR "invalidate"

/**
 * @def PROVISIONING_JSON_INVALIDATE_ADDRESS
 * @brief Address in flash memory where the provisioning JSON invalidate string is stored.
 *
 * This macro calculates the address by adding an offset to the start address of the provisioning JSON.
 * The offset is determined by subtracting 16 bytes from the total size of the provisioning JSON area (0x2000 bytes).
 */
#define PROVISIONING_JSON_INVALIDATE_ADDRESS (PROVISIONING_JSON_START_ADDRESS + 0x2000 - 16)

/**
 * @def PROVISIONING_JSON_INVALIDATE_LEN
 * @brief Length of the provisioning JSON invalidate string.
 */
#define PROVISIONING_JSON_INVALIDATE_LEN (strlen(PROVISIONING_JSON_INVALIDATE_STR))


/* Macros -------------------------------------------------------------*/
/**
 * @brief Computes the SHA-1 hash of the input data.
 *
 * This macro wraps the SM_sha1 function to compute the SHA-1 digest of the provided input buffer.
 *
 * @param IN   Pointer to the input data buffer.
 * @param LEN  Length of the input data in bytes.
 * @param OUT  Pointer to the output buffer where the SHA-1 hash (20 bytes) will be stored.
 */
#define COMPUTE_SHA1(IN, LEN, OUT)      SM_sha1(IN, LEN, OUT) 

/**
 * @def READER_CERTIF_BUF
 * @brief Macro to access the certificate data buffer from the provisioning structure stored in flash memory.
 *
 * This macro retrieves the pointer to the certificate data by calling the function getProvisioningFromFlash()
 * and accessing its certificate_data member. It is typically used to obtain the reader's certificate for
 * authentication or secure communication purposes.
 */
#define READER_CERTIF_BUF     (getProvisioningFromFlash()->certificate_data)

/**
 * @def READER_CERTIF_LEN
 * @brief Macro that retrieves the length of the reader's certificate from provisioning data stored in flash memory.
 *
 * This macro accesses the certificate length field (`certificate_len`) from the structure returned by
 * `getProvisioningFromFlash()`. It is used to determine the size of the reader's certificate for provisioning purposes.
 */
#define READER_CERTIF_LEN     (getProvisioningFromFlash()->certificate_len)

/**
 * @def READER_PUB_KEY_BUF
 * @brief Macro to access the reader's public key buffer from provisioning data stored in flash memory.
 *
 * This macro retrieves the pointer to the reader's public key by calling
 * getProvisioningFromFlash() and accessing its member 'readerPubK'.
 * It is typically used for cryptographic operations involving the reader's public key.
 */
#define READER_PUB_KEY_BUF    (getProvisioningFromFlash()->readerPubK)

/**
 * @def PROVISIONING_LOG
 * @brief Logs provisioning-related information using the application logging mechanism.
 *
 * This macro wraps the LOG_INFO_APP function to provide a standardized way to log
 * messages related to provisioning. It accepts a variable number of arguments,
 * allowing formatted logging similar to printf.
 *
 * Usage:
 *   PROVISIONING_LOG("Provisioning started for device: %s", device_id);
 *
 * @param ... Variable arguments to be passed to LOG_INFO_APP.
 */
#define PROVISIONING_LOG(...)    do{HAL_Delay(2);LOG_INFO_APP(__VA_ARGS__);}while(0)


/* Variables ---------------------------------------------------------*/
/**
 * @brief Maximum allowed length for JSON strings.
 */
uint32_t JSON_MAX_LEN = 1200;

/**
 * @brief Flag indicating whether the conversion has been confirmed.
 */
bool confirm_conversion = false;



/* Public functions -----------------------------------------------*/

/**
 * @brief Converts a hex string to a hex buffer.
 * @param p Pointer to the hex string.
 * @param len Length of the string.
 * @param hexp Pointer to the hex buffer.
 */
void hexStrToHex(char *p, uint32_t len, uint8_t *hexp){
  if (len%2)
  {
    hexp = NULL;
    return; 
  }
  for (size_t i = 0; i < len/2; i++)
  {
    char str[3];
    str[0] = p[(2*i)];
    str[1] = p[(2*i)+1];
    str[2] = '\0';
    hexp[i] = strtol(str, NULL, 16);
  }
}

/**
 * Find the first occurrence of find in s, where the search is limited to the
 * first slen characters of s.
 */
char *
strnstr(const char *s, const char *find, size_t slen)
{
  const char *tmp_s = s;
	char c, sc;
	size_t len;

	if ((c = *find++) != '\0') {
		len = strnlen(find, slen);
		do {
			do {
				// if (slen-- < 1 || (sc = *s++) == '\0')
        if (slen-- < 1 || ((sc = *tmp_s++) == 0xFF)){
					return (NULL);
        }
			} while (sc != c);
			if (len > slen)
				return (NULL);
		} while (strncmp(tmp_s, find, len) != 0);
		tmp_s--;
	}
	return ((char *)tmp_s);
}


/**
 * @brief Prints a buffer in hexadecimal format.
 * @param pstr Descriptive string.
 * @param pBuf Buffer to print.
 * @param nSize Size of the buffer.
 */
void printHex(char *pstr, uint8_t *pBuf, uint32_t nSize){
  PROVISIONING_LOG(pstr);
      for(uint32_t i=0;i<nSize;i++)
      {
        if(i%16 == 0)
        {
          PROVISIONING_LOG("\r\n %3d -> %3d - ", i, (i+15 < nSize)?(i+15):(nSize-1));
        }
        PROVISIONING_LOG("%.2x ", pBuf[i]);
      }
      PROVISIONING_LOG("\r\n");
}


/**
 * @brief Extracts data from a JSON string between start and stop markers.
 * @param Str Input string.
 * @param startStr Start marker string.
 * @param stopStr Stop marker string.
 * @param extStr Pointer to extracted string.
 * @return Length of extracted data.
 */
uint32_t extractJsonData(char *Str, char *startStr, char *stopStr, char **extStr){
  char *end;
  uint32_t len = 0;

  *extStr = strnstr(Str, startStr, JSON_MAX_LEN);
  if(*extStr == NULL)
  {
    *extStr = Str;
    return len;
  }
  
  *extStr += strnlen(startStr, JSON_MAX_LEN);
  if(*extStr)
  {
    end = strnstr(*extStr, stopStr, JSON_MAX_LEN);
    if(end)
    {
      len = end - *extStr;
    }
  }
  return len;
}


/**
 * @brief Checks if the JSON data in flash is valid.
 * @param jsonAddress Address of the JSON data.
 * @return true if valid, false otherwise.
 */
bool isValidFlashJson(uint32_t jsonAddress)
{
  uint32_t length;
  uint32_t pString = 0;
  char **string= (char **)&pString;

  length = extractJsonData((char *)jsonAddress, "reader_identifier\": \"", "\"", string);
  if (length == 0)
  {
    return false;
  }
  return true;
}


/**
 * @brief Checks if the provisioning structure is valid.
 * @param prov Pointer to provisioning structure.
 * @return true if valid, false otherwise.
 */
bool isValidProvisioning(provstruct_t *prov)
{
  if(prov->isValid == DEVICE_PROVISIONING_STRUCT_IS_VALID)
  {
  //is valid
    if(prov->struct_size == sizeof(provstruct_t))
    {
      PROVISIONING_LOG(TEXT_COLOR_GREEN);
      PROVISIONING_LOG("Provisioning info is valid\r\n");
      PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
      //print b1VendorSpecificExtension
      PROVISIONING_LOG("b1VendorSpecificExtension: \"%s\"\r\n", prov->b1VendorSpecificExtension);
      return true;
    }
    else
    {
      PROVISIONING_LOG(TEXT_COLOR_RED);
      PROVISIONING_LOG("Provisioning info is present, but wrong version\r\n");
      PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
      return false;
    }
  }
  else
  {
    PROVISIONING_LOG(TEXT_COLOR_RED);
    PROVISIONING_LOG("Provisioning info is not valid\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    return false;
  }
}

/**
 * @brief Shows the status of the JSON provisioning data.
 */
void showJsonStatus()
{
  if(!strncmp(PROVISIONING_JSON_INVALIDATE_STR, (void*)PROVISIONING_JSON_INVALIDATE_ADDRESS, PROVISIONING_JSON_INVALIDATE_LEN)){
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    PROVISIONING_LOG("\r\nOld provisioning json in flash\r\n\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
  }
  else if(isValidFlashJson(PROVISIONING_JSON_START_ADDRESS)){
    PROVISIONING_LOG(TEXT_COLOR_YELLOW);
    PROVISIONING_LOG("\r\nNew provisioning json in flash...\r\n");
    PROVISIONING_LOG("if you want to convert press 'c'\r\n\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
  }
  else{
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    PROVISIONING_LOG("No provisioning json in flash...\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
  }
}


/**
 * @brief Shows the status of the provisioning data.
 */
void showProvisioningStatus()
{
  provstruct_t *temp;
  temp = getProvisioningFromFlash();
  isValidProvisioning(temp);

  // if(temp->isValid == DEVICE_PROVISIONING_STRUCT_IS_VALID)
  // {
  //   //is valid
  //   if(temp->struct_size == sizeof(provstruct_t))
  //   {
  //     PROVISIONING_LOG("Provisioning info is valid\r\n");
  //     //print b1VendorSpecificExtension
  //     PROVISIONING_LOG("b1VendorSpecificExtension: \"%s\"\r\n", temp->b1VendorSpecificExtension);
  //   }
  //   else
  //   {
  //     PROVISIONING_LOG("Provisioning info is present, but wrong version\r\n");
  //   }
  // }
  // else
  // {
  //   PROVISIONING_LOG("Provisioning info is not valid\r\n");
  // }
}


/**
 * @brief Saves the provisioning data to flash memory.
 * @param profile_num Profile number to save.
 */
void saveProvisioningToFlash(uint32_t profile_num)
{
  
  switch (profile_num)
  {
    case 1: // without certificate
    {
      provstruct_t temp = {
          .isValid = DEVICE_PROVISIONING_STRUCT_IS_VALID,
          .readerID = READER_ID_2,
          .readerPubK = READER_PUB_KEY_2,
          .staticPrivateKey = READER_PRIVATE_KEY_2,
          .group_resolving_key = GROUP_RESOLVING_KEY,
          .certificate_data = {0},
          .certificate_len = 0,
          .readerIssuerPubK = {0}, //READER_ISSUER_PUB_KEY_2,
          .b1VendorSpecificExtension = B1_VENDOR_SPECIFIC_EXTENSION_2,
          .b1VendorSpecificExtensionLen = B1_VENDOR_SPECIFIC_EXTENSION_LEN_2,
          .reservedPairingKeys = {0},
          .numDevices = 1,
          .devices = 
          {
            {
              .name = ENDPOINT_NAME_2,
              .endpoint_Keyslot = ENDPOINT_KEYSLOT_2,
              .endpoint_PubK = ENDPOINT_PUBLIC_KEY_2,
              .endpoint_PrivK = ENDPOINT_PRIVATE_KEY_2,
            }
          },
          .isValidReserved = 0,
          .struct_size = sizeof(provstruct_t),
        };

      if(temp.certificate_len == 1)
      {
        temp.certificate_len = 0;
      }
      
      if(is_zeros(temp.devices[0].endpoint_Keyslot, AUTH_KEYSLOT_LEN_BYTES)) //keyslot null
      {
        uint8_t tmppubkey[AUTH_PUBKEY_LEN_BYTES+1];
        uint8_t tmpsha1[20];
        tmppubkey[0] = 0x04; //prepending 04 to pubkey
        memcpy(tmppubkey+1, temp.devices[0].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES);
        if (COMPUTE_SHA1(tmppubkey, AUTH_PUBKEY_LEN_BYTES+1, tmpsha1) == SM_Crypto_OK)
        {
          memcpy(temp.devices[0].endpoint_Keyslot, tmpsha1, AUTH_KEYSLOT_LEN_BYTES);
        } else {
          // TODO: manage error in computation of the slot
        }
      }
      PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
      EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
      
      
      PROVISIONING_LOG("Provisioning info without certificate saved to flash\r\n");

      break;
    }

    case 2: // with certificate
    {
      uint8_t cert_data[] = READER_CERTIFICATE;
      UNUSED(cert_data);
      provstruct_t temp = {
          .isValid = DEVICE_PROVISIONING_STRUCT_IS_VALID,
          .readerID = READER_ID,
          .readerPubK = READER_PUB_KEY,
          .group_resolving_key = GROUP_RESOLVING_KEY,
          .certificate_data = READER_CERTIFICATE,
          .certificate_len = (sizeof(cert_data)),
          .staticPrivateKey = READER_PRIVATE_KEY,
          .readerIssuerPubK = READER_ISSUER_PUB_KEY,
          .b1VendorSpecificExtension = B1_VENDOR_SPECIFIC_EXTENSION,
          .b1VendorSpecificExtensionLen = B1_VENDOR_SPECIFIC_EXTENSION_LEN,
          .reservedPairingKeys = {0},
          .numDevices = 1,
          .devices = 
          {
            {
              .name = ENDPOINT_NAME,
              .endpoint_Keyslot = ENDPOINT_KEYSLOT,
              .endpoint_PubK = ENDPOINT_PUBLIC_KEY,
              .endpoint_PrivK = ENDPOINT_PRIVATE_KEY,
            }
          },
          .isValidReserved = 0,
          .struct_size = sizeof(provstruct_t),
        };
      
      if(temp.certificate_len == 1)
      {
        temp.certificate_len = 0;
      }

      if(is_zeros(temp.devices[0].endpoint_Keyslot, AUTH_KEYSLOT_LEN_BYTES)) //keyslot null
      {
        uint8_t tmppubkey[AUTH_PUBKEY_LEN_BYTES+1];
        uint8_t tmpsha1[20];
        tmppubkey[0] = 0x04; //prepending 04 to pubkey
        memcpy(tmppubkey+1, temp.devices[0].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES);
        if (COMPUTE_SHA1(tmppubkey, AUTH_PUBKEY_LEN_BYTES+1, tmpsha1) == SM_Crypto_OK)
        {
          memcpy(temp.devices[0].endpoint_Keyslot, tmpsha1, AUTH_KEYSLOT_LEN_BYTES);
        } else {
          // TODO: manage error in computation of the slot
        }
      }
      PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
      EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
      PROVISIONING_LOG("Provisioning info with certificate saved to flash\r\n");
      break;
    }



    case 3: // with long certificate
    {
      uint8_t cert_data[] = READER_LONG_CERTIFICATE;
      UNUSED(cert_data);
      provstruct_t temp = {
          .isValid = DEVICE_PROVISIONING_STRUCT_IS_VALID,
          .readerID = READER_ID,
          .readerPubK = READER_PUB_KEY,
          .group_resolving_key = GROUP_RESOLVING_KEY,
          .certificate_data = READER_LONG_CERTIFICATE,
          .certificate_len = (sizeof(cert_data)),
          .staticPrivateKey = READER_PRIVATE_KEY,
          .readerIssuerPubK = READER_ISSUER_PUB_KEY,
          .b1VendorSpecificExtension = B1_VENDOR_SPECIFIC_EXTENSION,
          .b1VendorSpecificExtensionLen = B1_VENDOR_SPECIFIC_EXTENSION_LEN,
          .reservedPairingKeys = {0},
          .numDevices = 1,
          .devices = 
          {
            {
              .name = ENDPOINT_NAME,
              .endpoint_Keyslot = ENDPOINT_KEYSLOT,
              .endpoint_PubK = ENDPOINT_PUBLIC_KEY,
              .endpoint_PrivK = ENDPOINT_PRIVATE_KEY,
            }
          },
          .isValidReserved = 0,
          .struct_size = sizeof(provstruct_t),
        };

      if(temp.certificate_len == 1)
      {
        temp.certificate_len = 0;
      }
      
      if(is_zeros(temp.devices[0].endpoint_Keyslot, AUTH_KEYSLOT_LEN_BYTES)) //keyslot null
      {
        uint8_t tmppubkey[AUTH_PUBKEY_LEN_BYTES+1];
        uint8_t tmpsha1[20];
        tmppubkey[0] = 0x04; //prepending 04 to pubkey
        memcpy(tmppubkey+1, temp.devices[0].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES);
        if (COMPUTE_SHA1(tmppubkey, AUTH_PUBKEY_LEN_BYTES+1, tmpsha1) == SM_Crypto_OK)
        {
          memcpy(temp.devices[0].endpoint_Keyslot, tmpsha1, AUTH_KEYSLOT_LEN_BYTES);
        } else {
          // TODO: manage error in computation of the slot
        }
        memcpy(temp.devices[0].endpoint_Keyslot, tmpsha1, AUTH_KEYSLOT_LEN_BYTES);
      }
      PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
      EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
      PROVISIONING_LOG("Provisioning info with LONG certificate saved to flash\r\n");
      break;
    }




    case 4: // with short certificate
    {
      uint8_t cert_data[] = READER_SHORT_CERTIFICATE;
      UNUSED(cert_data);
      provstruct_t temp = {
          .isValid = DEVICE_PROVISIONING_STRUCT_IS_VALID,
          .readerID = READER_ID,
          .readerPubK = READER_PUB_KEY,
          .group_resolving_key = GROUP_RESOLVING_KEY,
          .certificate_data = READER_SHORT_CERTIFICATE,
          .certificate_len = (sizeof(cert_data)),
          .staticPrivateKey = READER_PRIVATE_KEY,
          .readerIssuerPubK = READER_ISSUER_PUB_KEY,
          .b1VendorSpecificExtension = B1_VENDOR_SPECIFIC_EXTENSION,
          .b1VendorSpecificExtensionLen = B1_VENDOR_SPECIFIC_EXTENSION_LEN,
          .reservedPairingKeys = {0},
          .numDevices = 1,
          .devices = 
          {
            {
              .name = ENDPOINT_NAME,
              .endpoint_Keyslot = ENDPOINT_KEYSLOT,
              .endpoint_PubK = ENDPOINT_PUBLIC_KEY,
              .endpoint_PrivK = ENDPOINT_PRIVATE_KEY,
            }
          },
          .isValidReserved = 0,
          .struct_size = sizeof(provstruct_t),
        };

      if(temp.certificate_len == 1)
      {
        temp.certificate_len = 0;
      }
      
      if(is_zeros(temp.devices[0].endpoint_Keyslot, AUTH_KEYSLOT_LEN_BYTES)) //keyslot null
      {
        uint8_t tmppubkey[AUTH_PUBKEY_LEN_BYTES+1];
        uint8_t tmpsha1[20];
        tmppubkey[0] = 0x04; //prepending 04 to pubkey
        memcpy(tmppubkey+1, temp.devices[0].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES);
        if (COMPUTE_SHA1(tmppubkey, AUTH_PUBKEY_LEN_BYTES+1, tmpsha1) == SM_Crypto_OK)
        {
          memcpy(temp.devices[0].endpoint_Keyslot, tmpsha1, AUTH_KEYSLOT_LEN_BYTES);
        } else {
          // TODO: manage error in computation of the slot
        }
      }
      PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
      EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
      PROVISIONING_LOG("Provisioning info with SHORT certificate saved to flash\r\n");
      break;
    }

    default:
    {
      PROVISIONING_LOG("Not valid choice\r\n");
    }
  }
}


/**
 * @brief Retrieves the provisioning data from flash memory.
 * @return Pointer to the provisioning structure.
 */
provstruct_t* getProvisioningFromFlash()
{
  return (provstruct_t*)PROVISIONING_START_ADDRESS;
}


/**
 * @brief Converts default provisioning data from JSON format.
 */
void convertDefaultProvisioningFromJson()
{
  convertProvisioningFromJson(PROVISIONING_JSON_START_ADDRESS);
}


/**
 * @brief Loads provisioning data from a JSON address.
 * @param jsonAddress Address of the JSON data.
 * @param provstruct Pointer to the provisioning structure to fill.
 */
void loadProvisionFromJson(uint32_t jsonAddress, provstruct_t*provstruct)
{
  uint32_t length;
  uint32_t pString = 0;
  char **string= (char **)&pString;
  
  //  {
  //    "MaxDevices": 8,

  provstruct->isValidReserved = 0;

  /* ---------- reader_identifier ---------- */
  //    "reader_identifier": "C90AB72872B5D77B43ECE5D565A41A9D5AD478A5071EB1A77125EF41A0D1C438",
  length = extractJsonData((char *)jsonAddress, "reader_identifier\": \"", "\"", string);
  if (length == 0)
  {
    PROVISIONING_LOG(TEXT_COLOR_RED);
    PROVISIONING_LOG("reader_identifier field empty\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    return;
  }
  hexStrToHex(*string, length, provstruct->readerID);
  printHex("reader_identifier: ", provstruct->readerID, length/2);

  /* ---------- reader_PrivK ---------- */
  //    "reader_PrivK": "1A39E361B0DB1915C2510BD92F3DBEB319ED68B16A0294347629D2E4BECDB599",
  length = extractJsonData(*string + length, "reader_PrivK\": \"", "\"", string);
  if (length == 0)
  {
    PROVISIONING_LOG(TEXT_COLOR_RED);
    PROVISIONING_LOG("reader_PrivK field empty\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    return;
  }
  hexStrToHex(*string, length, provstruct->staticPrivateKey);
  printHex("reader_PrivK: ", provstruct->staticPrivateKey, length/2);

  /* ---------- reader_PubK ---------- */
  //    "reader_PubK": "041A39E361B0DB1915C2510BD92F3DBEB319ED68B16A0294347629D2E4BECDB5991A39E361B0DB1915C2510BD92F3DBEB319ED68B16A0294347629D2E4BECDB599",
  length = extractJsonData(*string + length, "reader_PubK\": \"", "\"", string);
  if (length == 0)
  {
    PROVISIONING_LOG(TEXT_COLOR_RED);
    PROVISIONING_LOG("reader_PubK field empty\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    return;
  }
  hexStrToHex((char*)(*string+2), length-2, provstruct->readerPubK);
  printHex("reader_PubK: ", provstruct->readerPubK, (length-2)/2);

  /* ---------- group_resolving_key ---------- */
  //    "group_resolving_key": "041A39E361B0DB1915C2510BD92F3DBEB319ED68B16A0294347629D2E4BECDB5991A39E361B0DB1915C2510BD92F3DBEB319ED68B16A0294347629D2E4BECDB599",
  length = extractJsonData(*string + length, "group_resolving_key\": \"", "\"", string);
  if (length == 0)
  {
    PROVISIONING_LOG(TEXT_COLOR_YELLOW);
    PROVISIONING_LOG("group_resolving_key field empty\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    memset(provstruct->group_resolving_key, 0, GROUP_RESOLVING_KEY_SIZE_BYTES);
  }
  else
  {
    hexStrToHex((char*)(*string), length, provstruct->group_resolving_key);
    printHex("group_resolving_key: ", provstruct->group_resolving_key, (length)/2);
  }
  

  /* ---------- certificate_data + readerIssuerPubK ---------- */
  //    "certificate_data": "3081a30402000030819c830d3235303530353030303030305a85420004842242f6182ba1c1138d32b77fb9f7f37b70034b9f04443a5bea3c188beadb36490a7e95f91a4c162acfc3401c3a4f4e5a59251d45243ac8544a665cb951422f864700304402205a15bb0cd0718e077815fe8c71ddb05378c89fbf5ae2f976f2b506fcc224fa0402201aae5e32782dd979e71c8e1e6ba31054b121ac77933a4a7b3cf10e97cb64b9fe",
  length = extractJsonData(*string + length, "certificate_data\": \"", "\"", string);
  if (length == 0)
  {
    PROVISIONING_LOG(TEXT_COLOR_YELLOW);
    PROVISIONING_LOG("certificate_data field empty\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    provstruct->certificate_len = 0;
  }
  else
  {
    hexStrToHex(*string, length, provstruct->certificate_data);
    provstruct->certificate_len = length / 2;
    printHex("certificate_data: ", provstruct->certificate_data, provstruct->certificate_len);

    length = extractJsonData(*string + length, "readerIssuerPubK\": \"", "\"", string);
    if (length == 0)
    {
      PROVISIONING_LOG(TEXT_COLOR_YELLOW);
      PROVISIONING_LOG("readerIssuerPubK field empty\r\n");
      PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    }
    else
    {
      hexStrToHex((char *)(*string + 2), length - 2, provstruct->readerIssuerPubK);
      printHex("readerIssuerPubK: ", provstruct->readerIssuerPubK, (length - 2) / 2);
    }
  }

  /* ---------- b1VendorSpecificExtension ---------- */
  //    "b1VendorSpecificExtension": "Demo-3 Test",
  length = extractJsonData(*string + length, "b1VendorSpecificExtension\": \"", "\"", string);
  if (length == 0)
  {
    PROVISIONING_LOG(TEXT_COLOR_YELLOW);
    PROVISIONING_LOG("b1VendorSpecificExtension field empty\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    provstruct->b1VendorSpecificExtensionLen = 0;
  }
  else
  {
    if (length >= B1_VENDOR_SPEC_EXT_SIZE_BYTES)
      length = B1_VENDOR_SPEC_EXT_SIZE_BYTES - 1;
    memcpy(provstruct->b1VendorSpecificExtension, *string, length);
    provstruct->b1VendorSpecificExtension[length] = 0;
    provstruct->b1VendorSpecificExtensionLen = length;
    PROVISIONING_LOG("b1VendorSpecificExtension: %s\r\n", provstruct->b1VendorSpecificExtension);
  }

  /* ---------- Devices array ---------- */

  provstruct->numDevices = 0;

  /* Find `"Devices": [` */
  char *devicesBlock = strnstr(*string + length, "Devices\": [", JSON_MAX_LEN);
  if (devicesBlock == NULL)
  {
    PROVISIONING_LOG(TEXT_COLOR_YELLOW);
    PROVISIONING_LOG("Devices array not found in JSON\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    /* allowed to have 0 devices; keep going */
  }
  else
  {
    devicesBlock += strlen("Devices\": [");
    char *cursor = devicesBlock;
    uint32_t devIndex = 0;

    while (devIndex < NUM_PROV_DEVICES)
    {
      /* Find next '{' = start of device object */
      char *devStart = strnstr(cursor, "{", JSON_MAX_LEN);
      if (devStart == NULL)
      {
        break; /* no more devices */
      }

      /* Stop if ']' (end of array) is before the next '{' */
      char *arrayEnd = strnstr(cursor, "]", JSON_MAX_LEN);
      if ((arrayEnd != NULL) && (arrayEnd < devStart))
      {
        break;
      }

      devices_t *pdev = &provstruct->devices[devIndex];
      memset(pdev, 0, sizeof(*pdev));

      /* Name */
      length = extractJsonData(devStart, "Name\": \"", "\"", string);
      if (length == 0)
      {
        PROVISIONING_LOG("Error json... missing Name in device %lu\r\n",
                         (unsigned long)devIndex);
        break;
      }
      if (length >= sizeof(pdev->name))
        length = sizeof(pdev->name) - 1;
      memcpy(pdev->name, *string, length);
      pdev->name[length] = 0;
      PROVISIONING_LOG("Device[%lu] name: %s\r\n",
                       (unsigned long)devIndex, pdev->name);

      /* endpoint_Keyslot (optional; may be zeros) */
      length = extractJsonData(devStart, "endpoint_Keyslot\": \"", "\"", string);
      if (length == 0)
      {
        PROVISIONING_LOG(TEXT_COLOR_YELLOW);
        PROVISIONING_LOG("endpoint_Keyslot field empty for device %s\r\n", pdev->name);
        PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
        memset(pdev->endpoint_Keyslot, 0, AUTH_KEYSLOT_LEN_BYTES);
      }
      else
      {
        hexStrToHex(*string, length, pdev->endpoint_Keyslot);
        printHex("Device endpoint_Keyslot: ",
                 pdev->endpoint_Keyslot, length / 2);
      }

      /* endpoint_PubK (mandatory) */
      length = extractJsonData(devStart, "endpoint_PubK\": \"", "\"", string);
      if (length == 0)
      {
        PROVISIONING_LOG("Error json... missing endpoint_PubK in device %s\r\n", pdev->name);
        break;
      }
      hexStrToHex((char *)(*string + 2), length - 2, pdev->endpoint_PubK);
      printHex("Device endpoint_PubK: ",
               pdev->endpoint_PubK, (length - 2) / 2);

      /* Generate Keyslot from endpoint_PubK if all zeros */
      PROVISIONING_LOG(TEXT_COLOR_CYAN);
      PROVISIONING_LOG("Generating Keyslot ... \r\n");
      PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
      if (is_zeros(pdev->endpoint_Keyslot, AUTH_KEYSLOT_LEN_BYTES))
      {
        uint8_t tmppubkey[AUTH_PUBKEY_LEN_BYTES + 1];
        uint8_t tmpsha1[20];
        tmppubkey[0] = 0x04;
        memcpy(tmppubkey + 1, pdev->endpoint_PubK, AUTH_PUBKEY_LEN_BYTES);
        if (COMPUTE_SHA1(tmppubkey, AUTH_PUBKEY_LEN_BYTES + 1, tmpsha1) == SM_Crypto_OK)
        {
          memcpy(pdev->endpoint_Keyslot, tmpsha1, AUTH_KEYSLOT_LEN_BYTES);
        }
        else
        {
          PROVISIONING_LOG(TEXT_COLOR_RED);
          PROVISIONING_LOG("Error computing Keyslot for device %s\r\n", pdev->name);
          PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
        }
      }
      printHex("Device endpoint_Keyslot: ",
               pdev->endpoint_Keyslot, AUTH_KEYSLOT_LEN_BYTES);

      /* endpoint_PrivK (optional) */
      length = extractJsonData(devStart, "endpoint_PrivK\": \"", "\"", string);
      if (length == 0)
      {
        PROVISIONING_LOG(TEXT_COLOR_YELLOW);
        PROVISIONING_LOG("endpoint_PrivK field empty for device %s\r\n", pdev->name);
        PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
        memset(pdev->endpoint_PrivK, 0, sizeof(pdev->endpoint_PrivK));
      }
      else
      {
        hexStrToHex(*string, length, pdev->endpoint_PrivK);
        printHex("Device endpoint_PrivK: ",
                 pdev->endpoint_PrivK, length / 2);
      }

      devIndex++;

      /* Move cursor after this device object to search for the next one */
      char *devEnd = strnstr(devStart, "}", JSON_MAX_LEN);
      if (devEnd == NULL)
      {
        break;
      }
      cursor = devEnd + 1;
    }

    provstruct->numDevices = devIndex;
  }

  provstruct->isValid = DEVICE_PROVISIONING_STRUCT_IS_VALID;
  provstruct->struct_size = sizeof(provstruct_t);
}


/**
 * @brief Converts provisioning data from JSON format.
 * @param jsonAddress Address of the JSON data.
 */
void convertProvisioningFromJson(uint32_t jsonAddress)
{
  bool isInvalidate = false;
  if(!confirm_conversion)
  {
    if(!strncmp(PROVISIONING_JSON_INVALIDATE_STR, (void*)PROVISIONING_JSON_INVALIDATE_ADDRESS, PROVISIONING_JSON_INVALIDATE_LEN)){
      PROVISIONING_LOG(TEXT_COLOR_YELLOW);
      PROVISIONING_LOG("Provisioning json invalidated.... if you want to convert anyway, press \"c\" again\r\n");
      PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
      confirm_conversion = true;
      return;
    }
  }
  else{
      PROVISIONING_LOG(TEXT_COLOR_YELLOW);
      PROVISIONING_LOG("Converting json invalidated...\r\n");
      PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
      isInvalidate = true;
      confirm_conversion = false;
  }

  provstruct_t temp = { 0 };
  loadProvisionFromJson(jsonAddress, &temp);
  if(temp.isValid == DEVICE_PROVISIONING_STRUCT_IS_VALID)
  {
    PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
    EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
    
    //invalidate flash json page write "invalidate at the end of the page"
    if(!isInvalidate){
      PROVISIONING_LOG("WriteToFlash() for invalidate json\r\n\r\n\r\n");
      WriteToFlash(PROVISIONING_JSON_INVALIDATE_STR, PROVISIONING_JSON_INVALIDATE_LEN, PROVISIONING_JSON_INVALIDATE_ADDRESS);
    }
  }
}


/**
 * @brief Converts a buffer to a hexadecimal string.
 * @param str Output string buffer.
 * @param buff Input buffer.
 * @param len Length of input buffer.
 */
void sprintfHex(char *str, uint8_t *buff, uint32_t len){
  str[0] = 0;
  for (size_t i = 0; i < len; i++)
  {
    if(sprintf(&str[2*i], "%.2x", buff[i])==0)
    {
      break;
    }
  }  
}

/**
 * @brief Print a buffer as a C array #define
 * @param titl_str Name of the array.
 * @param buff Input buffer.
 * @param len Length of input buffer.
 */
void printfHex0x(char *titl_str, uint8_t *buff, uint32_t len){
  PROVISIONING_LOG("%s", titl_str);
  PROVISIONING_LOG(" { ");
  for (size_t i = 0; i < len; i++)
  {
    PROVISIONING_LOG("0x%.2x, ", buff[i]);
  }
  PROVISIONING_LOG("}");
}

/**
 * @brief Prints the device JSON data.
 */
void printDeviceJson()
{
  provstruct_t* temp = getProvisioningFromFlash();
  if (!isValidProvisioning(temp))
  {
    PROVISIONING_LOG(TEXT_COLOR_RED);
    PROVISIONING_LOG("Error print provisioning: Provisioning not valid\r\n");
    PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
    return;
  }
  
  char tmpbuf[(256*2) + 1]; //DB1D6524990AECCE49C3C5C79EE41361B01B49302EAC1CC5F9DECD0D1058D14E

  PROVISIONING_LOG(TEXT_COLOR_GREEN);
  PROVISIONING_LOG("\r\nJson for reader provisioning:\r\n");
  PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
  PROVISIONING_LOG("{\r\n");
  PROVISIONING_LOG("    \"MaxDevices\": 8,\r\n");

  sprintfHex(tmpbuf, temp->readerID, sizeof(temp->readerID));
  PROVISIONING_LOG("    \"reader_identifier\": \"%s\",\r\n", tmpbuf);

  sprintfHex(tmpbuf, temp->staticPrivateKey, sizeof(temp->staticPrivateKey));
  PROVISIONING_LOG("    \"reader_PrivK\": \"%s\",\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->readerPubK, sizeof(temp->readerPubK));
  PROVISIONING_LOG("    \"reader_PubK\": \"04%s\",\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->group_resolving_key, sizeof(temp->group_resolving_key));
  PROVISIONING_LOG("    \"group_resolving_key\": \"%s\",\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->readerIssuerPubK, sizeof(temp->readerIssuerPubK));
  PROVISIONING_LOG("    \"readerIssuerPubK\": \"04%s\",\r\n", tmpbuf);

  PROVISIONING_LOG("    \"certificate_data\": \"");
  uint32_t tmpcertificate_len = temp->certificate_len;
  uint32_t nbytes = 0;
  uint32_t noffset = 0;
  while(tmpcertificate_len>0)
  {
    if(tmpcertificate_len>100)
    {
      nbytes = 100;
    }
    else
    {
      nbytes = tmpcertificate_len;
    }
    sprintfHex(tmpbuf, temp->certificate_data + noffset, nbytes);
    PROVISIONING_LOG("%s", tmpbuf); //print certificate_data standalone for lenght reason
    noffset += nbytes;
    tmpcertificate_len -= nbytes;
  }
  PROVISIONING_LOG("\",\r\n");

//  sprintfHex(tmpbuf, temp->b1VendorSpecificExtension, temp->b1VendorSpecificExtensionLen);
  PROVISIONING_LOG("    \"b1VendorSpecificExtension\": \"%s\",\r\n", temp->b1VendorSpecificExtension);
  
  PROVISIONING_LOG("    \"Devices\": [\r\n");

  PROVISIONING_LOG("        {\r\n");
//  sprintfHex(tmpbuf, temp->devices[0].name, sizeof(temp->devices[0].name));
  PROVISIONING_LOG("            \"Name\": \"%s\",\r\n", temp->devices[0].name);
  sprintfHex(tmpbuf, temp->devices[0].endpoint_Keyslot, sizeof(temp->devices[0].endpoint_Keyslot));
  PROVISIONING_LOG("            \"endpoint_Keyslot\": \"%s\"\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->devices[0].endpoint_PubK, sizeof(temp->devices[0].endpoint_PubK));
  PROVISIONING_LOG("            \"endpoint_PubK\": \"04%s\"\r\n", tmpbuf);
  PROVISIONING_LOG("        },\r\n");
  PROVISIONING_LOG("        {\r\n");
//  sprintfHex(tmpbuf, temp->devices[1].name, sizeof(temp->devices[1].name));
  PROVISIONING_LOG("            \"Name\": \"%s\",\r\n", temp->devices[1].name);
  sprintfHex(tmpbuf, temp->devices[1].endpoint_Keyslot, sizeof(temp->devices[1].endpoint_Keyslot));
  PROVISIONING_LOG("            \"endpoint_Keyslot\": \"%s\"\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->devices[1].endpoint_PubK, sizeof(temp->devices[1].endpoint_PubK));
  PROVISIONING_LOG("            \"endpoint_PubK\": \"04%s\"\r\n", tmpbuf);
  PROVISIONING_LOG("        },\r\n");
  PROVISIONING_LOG("        {\r\n");
//  sprintfHex(tmpbuf, temp->devices[2].name, sizeof(temp->devices[2].name));
  PROVISIONING_LOG("            \"Name\": \"%s\",\r\n", temp->devices[2].name);
  sprintfHex(tmpbuf, temp->devices[2].endpoint_Keyslot, sizeof(temp->devices[2].endpoint_Keyslot));
  PROVISIONING_LOG("            \"endpoint_Keyslot\": \"%s\"\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->devices[2].endpoint_PubK, sizeof(temp->devices[2].endpoint_PubK));
  PROVISIONING_LOG("            \"endpoint_PubK\": \"04%s\"\r\n", tmpbuf);
  PROVISIONING_LOG("        },\r\n");
  PROVISIONING_LOG("        {\r\n");
//  sprintfHex(tmpbuf, temp->devices[3].name, sizeof(temp->devices[3].name));
  PROVISIONING_LOG("            \"Name\": \"%s\",\r\n", temp->devices[3].name);
  sprintfHex(tmpbuf, temp->devices[3].endpoint_Keyslot, sizeof(temp->devices[3].endpoint_Keyslot));
  PROVISIONING_LOG("            \"endpoint_Keyslot\": \"%s\"\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->devices[3].endpoint_PubK, sizeof(temp->devices[3].endpoint_PubK));
  PROVISIONING_LOG("            \"endpoint_PubK\": \"04%s\"\r\n", tmpbuf);
  PROVISIONING_LOG("        },\r\n");
  PROVISIONING_LOG("        {\r\n");
//  sprintfHex(tmpbuf, temp->devices[4].name, sizeof(temp->devices[4].name));
  PROVISIONING_LOG("            \"Name\": \"%s\",\r\n", temp->devices[4].name);
  sprintfHex(tmpbuf, temp->devices[4].endpoint_Keyslot, sizeof(temp->devices[4].endpoint_Keyslot));
  PROVISIONING_LOG("            \"endpoint_Keyslot\": \"%s\"\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->devices[4].endpoint_PubK, sizeof(temp->devices[4].endpoint_PubK));
  PROVISIONING_LOG("            \"endpoint_PubK\": \"04%s\"\r\n", tmpbuf);
  PROVISIONING_LOG("        },\r\n");
  
  PROVISIONING_LOG("    ]\r\n");
  PROVISIONING_LOG("}\r\n");


  PROVISIONING_LOG(TEXT_COLOR_GREEN);
  PROVISIONING_LOG("\r\nJson for device provisioning:\r\n");
  PROVISIONING_LOG(TEXT_COLOR_DEFAULT);

  PROVISIONING_LOG("{\r\n");
  PROVISIONING_LOG("    \"SelectVendorExtension\": \"ST Aliro demo\",\r\n");
  PROVISIONING_LOG("    \"Credentials\": [\r\n");
  PROVISIONING_LOG("        {\r\n");
  PROVISIONING_LOG("            \"Name\": \"%s\",\r\n", temp->devices[0].name);
  PROVISIONING_LOG("            \"MaxSubGrpReaders\": 8,\r\n");
  sprintfHex(tmpbuf, temp->devices[0].endpoint_PrivK, sizeof(temp->devices[0].endpoint_PrivK));
  PROVISIONING_LOG("            \"credential_PrivK\": \"%s\",\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->devices[0].endpoint_PubK, sizeof(temp->devices[0].endpoint_PubK));
  PROVISIONING_LOG("            \"credential_PubK\": \"04%s\",\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->readerID, sizeof(temp->readerID)/2);
  PROVISIONING_LOG("            \"reader_group_identifier\": \"%s\",\r\n", tmpbuf);
  
  if(temp->certificate_len == 0)
  {
    sprintfHex(tmpbuf, temp->readerPubK, sizeof(temp->readerPubK));
    PROVISIONING_LOG("            \"reader_PubK\": \"04%s\",\r\n", tmpbuf);
  }
  else
  {
    sprintfHex(tmpbuf, temp->readerIssuerPubK, sizeof(temp->readerIssuerPubK));
    PROVISIONING_LOG("            \"reader_issuer_PubK\": \"04%s\",\r\n", tmpbuf);
  }
  
  sprintfHex(tmpbuf, temp->group_resolving_key, sizeof(temp->group_resolving_key));
  PROVISIONING_LOG("            \"group_resolving_key\": \"%s\",\r\n", tmpbuf);
  
  PROVISIONING_LOG("            \"auth0ResponseVendorExtension\": \"Room X\"\r\n");
  PROVISIONING_LOG("        }\r\n");
  PROVISIONING_LOG("    ]\r\n");
  PROVISIONING_LOG("}\r\n");

  PROVISIONING_LOG("\r\n");

  PROVISIONING_LOG(TEXT_COLOR_GREEN);
  PROVISIONING_LOG("\r\nTo clone this provisioning to another board, use the following commands:\r\n");
  PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
  
  sprintfHex(tmpbuf, temp->readerID, sizeof(temp->readerID));
  PROVISIONING_LOG("setRDiden-%s\r\n", tmpbuf);

  sprintfHex(tmpbuf, temp->staticPrivateKey, sizeof(temp->staticPrivateKey));
  PROVISIONING_LOG("setRDprvk-%s\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->readerPubK, sizeof(temp->readerPubK));
  PROVISIONING_LOG("setRDpubk-04%s\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->group_resolving_key, sizeof(temp->group_resolving_key));
  PROVISIONING_LOG("setRDgrrk-%s\r\n", tmpbuf);
  sprintfHex(tmpbuf, temp->readerIssuerPubK, sizeof(temp->readerIssuerPubK));
  PROVISIONING_LOG("setCrIspk-04%s\r\n", tmpbuf);
  //loop over devices pubk
  for(uint8_t i=0;i<NUM_PROV_DEVICES;i++)
  {
    if(!is_zeros(temp->devices[i].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES))
    {
      sprintfHex(tmpbuf, temp->devices[i].endpoint_PubK, sizeof(temp->devices[i].endpoint_PubK));
      PROVISIONING_LOG("Access credential key -> %s\r\n", temp->devices[i].name);
      PROVISIONING_LOG("setDEVpbk-04%s\r\n", tmpbuf);
    }
  }

  PROVISIONING_LOG(TEXT_COLOR_GREEN);
  PROVISIONING_LOG("\r\nTo use this provisioning as default in the FW, use the following C arrays:\r\n");
  PROVISIONING_LOG(TEXT_COLOR_DEFAULT);
  
  printfHex0x("#define READER_ID_2                    ", temp->readerID, sizeof(temp->readerID));
  PROVISIONING_LOG("\r\n\r\n");
  printfHex0x("#define READER_PRIVATE_KEY_2           ", temp->staticPrivateKey, sizeof(temp->staticPrivateKey));
  PROVISIONING_LOG("\r\n\r\n");
  printfHex0x("#define READER_PUB_KEY_2               ", temp->readerPubK, sizeof(temp->readerPubK));
  PROVISIONING_LOG("\r\n\r\n");
  uint8_t cred_issuer_pub_key_bytes[] =   CREDENTIAL_ISSUER_PUB_KEY_2;
  printfHex0x("#define CREDENTIAL_ISSUER_PUB_KEY_2    ", cred_issuer_pub_key_bytes, sizeof(cred_issuer_pub_key_bytes));
//  sprintfHex0x("XX", temp->readerID, sizeof(temp->readerID));
  PROVISIONING_LOG("\r\n\r\n");
  printfHex0x("#define READER_ISSUER_PUB_KEY_2        ", temp->readerIssuerPubK, sizeof(temp->readerIssuerPubK));
  PROVISIONING_LOG("\r\n\r\n");
  //loop over devices pubk
  for(uint8_t i=0;i<NUM_PROV_DEVICES;i++)
  {
    if(!is_zeros(temp->devices[i].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES))
    {
      PROVISIONING_LOG("Access credential key -> %s\r\n", temp->devices[i].name);
      printfHex0x("#define ENDPOINT_PUBLIC_KEY_2	    ", temp->devices[i].endpoint_PubK, sizeof(temp->devices[i].endpoint_PubK));
      PROVISIONING_LOG("\r\n\r\n");
    }
  }
}


/**
 * @brief Checks the endpoint public key for provisioning.
 * @param endp_PubK Pointer to the endpoint public key.
 * @return Status code.
 */
uint8_t ProvisioningCheckEndpointPubK(uint8_t *endp_PubK)
{
  for(uint8_t i=0;i<NUM_PROV_DEVICES;i++)
  {
    if(0==memcmp(endp_PubK, (getProvisioningFromFlash()->devices[i].endpoint_PubK), AUTH_PUBKEY_LEN_BYTES))
    {
      return 0;
    }
  }
  return 1;
}


/**
 * @brief Checks the reader public key.
 * @return true if valid, false otherwise.
 */
bool CheckReaderPubK(void)
{
  showJsonStatus();

  if (!isValidProvisioning(getProvisioningFromFlash()))
  {
    PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_RED);
    PROVISIONING_LOG("\r\nBoard is not provisioned\r\n");
    PLATFORM_LOG(VERB_NORMAL, TEXT_COLOR_DEFAULT);
    PROVISIONING_LOG("Use 's' command to load default keys\r\n");
    PROVISIONING_LOG("Use 'c' command to convert keys from embedded json\r\n");
    PROVISIONING_LOG("Use '?' for help\r\n\r\n");
    return false;
  }
  else
  {
    PROVISIONING_LOG("Board provisioning ok\r\n");
    return true;
  }
}

/**
 * @brief Saves the reader public key to flash memory.
 * @param pkey Pointer to the public key.
 * @param ksize Size of the key.
 */
void saveRDPubKToFlash(const uint8_t *pkey, const uint8_t ksize)
{
  provstruct_t temp = { 0 };
  provstruct_t *prov;
  prov = getProvisioningFromFlash();
  isValidProvisioning(prov);
  // temp.readerPubK
  memcpy(&temp, prov, sizeof(provstruct_t));
  
//  for(uint8_t j = 0; j < ksize; j++)
//  {
//    prov->readerPubK[j] = pkey[j];
//  }
  
  memcpy(temp.readerPubK, pkey, ksize);

  // remove provisioning whith certificate
  memset(temp.certificate_data, 0, temp.certificate_len);
  temp.certificate_len = 0,
  memset(temp.readerIssuerPubK, 0, AUTH_PUBKEY_LEN_BYTES);  

  PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
  EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);

  //hexStrToHex((char*)(*string), length, temp->readerPubK);
  //printHex("reader_PubK: ", provstruct->readerPubK, (length-2)/2);

}

/**
 * @brief Saves the last access credential public key to the provisioning flash memory.
 *
 * This function attempts to save the provided public key (`last_pkey`) into a provisioning slot.
 * If the specified slot (`nSlot`) is invalid or occupied, it searches for the first available slot.
 * The function also computes a SHA-1 hash of the public key (with a prepended byte) to generate a keyslot,
 * if the keyslot is currently empty. The provisioning structure is then written back to flash memory.
 *
 * @param last_pkey Pointer to the public key to be saved.
 * @param ksize Size of the public key in bytes.
 * @param nSlot Desired slot index for saving the public key. If invalid, the function finds the first free slot.
 *
 * @note The function logs status messages and handles errors in slot allocation and SHA-1 computation.
 * @note The function assumes that `getProvisioningFromFlash`, `isValidProvisioning`, `is_zeros`, `COMPUTE_SHA1`, 
 *       and `EraseWriteToFlash` are implemented elsewhere.
 */
void saveAccessCredentialPuBK(uint8_t *pkey, const uint8_t ksize, uint8_t nSlot, uint8_t *pkeyname)
{
  provstruct_t temp = { 0 };
  provstruct_t *prov;
  prov = getProvisioningFromFlash();
  isValidProvisioning(prov);
  memcpy(&temp, prov, sizeof(provstruct_t));
  
  if (nSlot < NUM_PROV_DEVICES && is_zeros(getProvisioningFromFlash()->devices[nSlot].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES))
  {
    //slot ok
    PROVISIONING_LOG("Slot %d is empty and can be used\r\n", nSlot);
  }
  else
  {
    //slot not valid, find first free slot
    for(uint8_t i=0;i<NUM_PROV_DEVICES;i++)
    {
      if(is_zeros(getProvisioningFromFlash()->devices[i].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES))
      {
        nSlot = i;
        break;
      }
    }

    if (nSlot < NUM_PROV_DEVICES)
    {
      PROVISIONING_LOG("Slot %d empty was found and can be used\r\n", nSlot);
    }
    else
    {
      PROVISIONING_LOG("No free slot found\r\n");
      return;
    }
  }

  memcpy(temp.devices[nSlot].name, pkeyname, strlen((char const*)pkeyname));
  memcpy(temp.devices[nSlot].endpoint_PubK, pkey, 64);
    
  if(is_zeros(temp.devices[nSlot].endpoint_Keyslot, AUTH_KEYSLOT_LEN_BYTES)) //keyslot null
  {
    uint8_t tmppubkey[AUTH_PUBKEY_LEN_BYTES+1];
    uint8_t tmpsha1[20];
    tmppubkey[0] = 0x04; //prepending 04 to pubkey
    memcpy(tmppubkey+1, temp.devices[nSlot].endpoint_PubK, AUTH_PUBKEY_LEN_BYTES);
    if (COMPUTE_SHA1(tmppubkey, AUTH_PUBKEY_LEN_BYTES+1, tmpsha1) == SM_Crypto_OK)
    {
      memcpy(temp.devices[nSlot].endpoint_Keyslot, tmpsha1, AUTH_KEYSLOT_LEN_BYTES);
    } else {
      // TODO: manage error in computation of the slot
    }
  }

  PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
  EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);

  PROVISIONING_LOG("Last Access Credential PuBK saved to flash\r\n");
}

/**
 * @brief Saves the reader device's private key to flash memory.
 *
 * This function retrieves the current provisioning structure from flash,
 * validates it, updates the static private key field with the provided key,
 * and writes the updated structure back to flash memory.
 *
 * @param[in] pkey Pointer to the private key data to be saved.
 * @param[in] ksize Size of the private key in bytes.
 */
void saveRDPrvKToFlash(const uint8_t *pkey, const uint8_t ksize)
{
  provstruct_t temp = { 0 };
  provstruct_t *prov;
  prov = getProvisioningFromFlash();
  isValidProvisioning(prov);
  memcpy(&temp, prov, sizeof(provstruct_t));
  
  memcpy(temp.staticPrivateKey, pkey, ksize);

  PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
  EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
  PROVISIONING_LOG("Reader private key saved to flash\r\n");
}

/**
 * @brief Saves the reader identifier to flash memory.
 *
 * This function copies the provided reader identifier (`prid`) of size `ksize`
 * into a temporary provisioning structure, then writes it to flash memory at
 * the provisioning start address. The function first retrieves the current
 * provisioning data from flash, validates it, and then updates the reader ID.
 *
 * @param prid Pointer to the reader identifier data to be saved.
 * @param ksize Size of the reader identifier data in bytes.
 */
void saveRDidenToFlash(const uint8_t *prid, const uint8_t ksize)
{
  provstruct_t temp = { 0 };
  provstruct_t *prov;
  prov = getProvisioningFromFlash();
  isValidProvisioning(prov);
  memcpy(&temp, prov, sizeof(provstruct_t));

  memcpy(temp.readerID, prid, ksize);

  PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
  EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
  PROVISIONING_LOG("Reader identifier and sub-identifier saved to flash\r\n");
}

/**
 * @brief Saves the group resolving key to flash memory.
 *
 * This function retrieves the current provisioning structure from flash,
 * validates it, updates the group resolving key with the provided key,
 * and writes the updated structure back to flash memory.
 *
 * @param[in] pkey Pointer to the group resolving key to be saved.
 * @param[in] ksize Size of the group resolving key in bytes.
 */
void saveRDgrrkToFlash(const uint8_t *pkey, const uint8_t ksize)
{
  provstruct_t temp = { 0 };
  provstruct_t *prov;
  prov = getProvisioningFromFlash();
  isValidProvisioning(prov);
  memcpy(&temp, prov, sizeof(provstruct_t));

  memcpy(temp.group_resolving_key, pkey, ksize);

  PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
  EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
  PROVISIONING_LOG("Group resolving key saved to flash\r\n");
}

/**
 * @brief Saves the credential issuer's public key to flash memory.
 *
 * This function retrieves the current provisioning structure from flash,
 * updates the issuer's public key field with the provided key, and writes
 * the updated structure back to flash memory.
 *
 * @param[in] pkey   Pointer to the buffer containing the issuer's public key.
 * @param[in] ksize  Size (in bytes) of the public key to be saved.
 */
void saveCredIsPkToFlash(const uint8_t *pkey, const uint8_t ksize)
{
  provstruct_t temp = { 0 };
  provstruct_t *prov;
  prov = getProvisioningFromFlash();
  isValidProvisioning(prov);
  memcpy(&temp, prov, sizeof(provstruct_t));

  memcpy(temp.readerIssuerPubK, pkey, ksize);

  PROVISIONING_LOG("EraseWriteToFlash()...\r\n");
  EraseWriteToFlash(&temp, sizeof(temp), PROVISIONING_START_ADDRESS);
  PROVISIONING_LOG("Issuer public key saved to flash\r\n");
}

/**
 * @brief Checks if all bytes in a buffer are zero.
 *
 * This function iterates through a buffer of size @p nSize and checks if every byte is equal to 0x00.
 *
 * @param pBuf Pointer to the buffer to check.
 * @param nSize Number of bytes to check in the buffer.
 * @return uint8_t Returns 1 if all bytes are zero, 0 otherwise.
 */
uint8_t is_zeros(uint8_t* pBuf, uint32_t nSize)
{
  for(uint32_t i=0;i<nSize;i++)
  {
    if(*pBuf != 0x00)
    {
      return 0;
    }
  }
  return 1;
}

/**
 * @brief Retrieves the public key of an endpoint from a keyslot.
 *
 * This function searches through the provisioned devices to find a device whose
 * endpoint keyslot matches the provided keyslot. If a match is found, the function
 * sets the slotFound flag to 1. The public key copy operation is currently not implemented.
 *
 * @param[out] endp_PubK Pointer to the buffer where the endpoint public key should be copied.
 * @param[in]  endp_Keyslot Pointer to the keyslot to search for.
 * @param[out] slotFound Pointer to a flag that will be set to 1 if the keyslot is found, 0 otherwise.
 *
 * @return Always returns 0.
 */
uint8_t ProvisioningGetEndpointPubKFromKeyslot(uint8_t *endp_PubK, uint8_t *endp_Keyslot, uint8_t*slotFound)
{
  *slotFound = 0;
  //lookup endp_Keyslot
  //copy endp_PubK
  //*slotFound = 1;
  uint32_t storedDevices = getProvisioningFromFlash()->numDevices;
  for (uint32_t i = 0; i < storedDevices; i++)
  {
    devices_t *dev = &(getProvisioningFromFlash()->devices[i]);
    if (memcmp(&(dev->endpoint_Keyslot[0]), endp_Keyslot, AUTH_KEYSLOT_LEN_BYTES) == 0)
    {
      *slotFound = 1;
      break;
    }
  }
  return 0;
}
