/***************************************************************************//**
 * @file
 * @brief This file implements abstracting the PSA CRYPTO interface.
 *******************************************************************************
 * # License
 * <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * The licensor of this software is Silicon Laboratories Inc. Your use of this
 * software is governed by the terms of Silicon Labs Master Software License
 * Agreement (MSLA) available at
 * www.silabs.com/about-us/legal/master-software-license-agreement. This
 * software is distributed to you in Source Code format and is governed by the
 * sections of the MSLA applicable to Source Code.
 *
 ******************************************************************************/

#ifndef SECURITY_MANAGER_H_
#define SECURITY_MANAGER_H_

#include <psa/crypto_types.h>
#include <stdbool.h>
#include <string.h>
#include "psa/crypto.h"
#include "psa/crypto_values.h"

/**
 * Import a key into PSA ITS.
 *
 * The caller must Provide a valid key.
 *
 * Based on the parameters passed, the security manager will choose the storage location.
 * For persistent keys, key_id is managed by the application, and for volatiles keys,
 * PSA will create a key_id and returns the same to the application. All the keys will be wrapped
 * before storage and the key_id is further used for all the security operations.
 *
 * @param[out]  sl_psa_key_id                       Pointer to Key Id to be used for persistent keys. If the key is stored
 *                                                  in RAM, PSA will allocate the key ID and pass it to the application. For
 *                                                  non-volatile keys, key ID provided by application will be assigned as the reference.
 * @param[in]   sl_psa_key_type                     Key type encoding for the key.
 * @param[in]   sl_psa_key_algorithm                Key algorithm encoding for the key.
 * @param[in]   sl_psa_key_usage                    Key Usage encoding for the key.
 * @param[in]   sl_psa_key_persistence              What persistence level needs to be applied to the key.
 * @param[in]   sl_psa_key_literal                  Pointer to the actual key.
 * @param[in]   sl_key_literal_len                  Length of the key.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_import_key(psa_key_id_t *        sl_psa_key_id,
                                   psa_key_type_t        sl_psa_key_type,
                                   psa_algorithm_t       sl_psa_key_algorithm,
                                   psa_key_usage_t       sl_psa_key_usage,
                                   psa_key_persistence_t sl_psa_key_persistence,
                                   const uint8_t *       sl_psa_key_literal,
                                   size_t                sl_key_literal_len);

/**
 * Generates a key and stores the same in PSA.
 *
 * Based on the parameters passed, the security manager will choose the storage location.
 * For persistent keys, key_id is managed by the application, and for volatiles keys,
 * PSA will create a key_id and returns the same to the application. All the keys will be wrapped
 * before storage and the key_id is further used for all the security operations.
 *
 * @param[out]  sl_psa_key_id                       Pointer to Key Id to be used for persistent keys. If the key is stored
 *                                                  in RAM, PSA will allocate the key ID and pass it to the application. For
 *                                                  non-volatile keys, key ID provided by application will be assigned as the reference.
 * @param[in]   sl_psa_key_type                     Key type encoding for the key.
 * @param[in]   sl_psa_key_algorithm                Key algorithm encoding for the key.
 * @param[in]   sl_psa_key_usage                    Key Usage encoding for the key.
 * @param[in]   sl_psa_key_persistence              What persistence level needs to be applied to the key.
 * @param[in]   sl_psa_key_len                      Length of the key.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_generate_key(psa_key_id_t *        sl_psa_key_id,
                                     psa_key_type_t        sl_psa_key_type,
                                     psa_algorithm_t       sl_psa_key_algorithm,
                                     psa_key_usage_t       sl_psa_key_usage,
                                     psa_key_persistence_t sl_psa_key_persistence,
                                     size_t                sl_psa_key_len);

/**
 * Export a key from PSA ITS.
 *
 * This API can be used to export the key stored in the PSA. Only keys marked as exportable can be
 * read back from the storage as plaintext.
 *
 * @param[in]   sl_psa_key_id                       Key ID used as a reference to the key. This is either passed to import key,
 *                                                  for persistent keys, or returned to the application in the key context, for
 *                                                  volatile keys.
 * @param[in]   sl_psa_key_buffer                   Pointer to the buffer to store the key.
 * @param[in]   sl_psa_key_buffer_len               Length of the buffer.
 * @param[out]  sl_psa_key_len                      Length of the exported key.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_export_key(psa_key_id_t sl_psa_key_id,
                                   uint8_t *    sl_psa_key_buffer,
                                   size_t       sl_psa_key_buffer_len,
                                   size_t *     sl_psa_key_len);

/**
 * Get attributes for a key stored in PSA ITS.
 *
 * @param[in]   sl_psa_key_id                       Key ID used as a reference to the key.
 * @param[out]  sl_psa_key_attributes               output pointer for key attributes.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_get_key_attributes(psa_key_id_t          sl_psa_key_id,
                                           psa_key_attributes_t *sl_psa_key_attributes);

/**
 * Destroy a key stored in PSA ITS.
 *
 * This API can be used to dispose a stored key from PSA.
 *
 * @param[in]   sl_psa_key_id                       Key ID used as a reference to the key.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_destroy_key(psa_key_id_t sl_psa_key_id);

/**
 * Make a copy a key stored in the ITS.
 *
 * This API can be used to make copies of a key in ITS.
 * This API can also be used to change the key attributes of a existing key.
 *
 * If the source and destination key ids are the same, a copy of the original
 * key will be made with new additional attributes.
 *
 * If the source and destination keyids are different, a copy of source key is
 * made, and new attributes additional attributes, and the keyId of new key will
 * be returned.
 *
 * @note /ref psa_copy_key() only supports unwrapped source keys.
 * This API will fail if the sl_psa_source_key_id is wrapped.
 * @note There are restrictions on what attributes can be changed. Refer to /ref psa_copy_key()
 *
 * @param[in]       sl_psa_source_key_id            Key ID of the source key.
 * @param[in]       sl_psa_key_attributes           New key attributes to be added.
 * @param[inout]    sl_psa_dest_key_id              KeyId for new key.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_copy_key(psa_key_id_t          sl_psa_source_key_id,
                                 psa_key_attributes_t *sl_psa_key_attributes,
                                 psa_key_id_t         *sl_psa_dest_key_id);

/**
 * API to encrypt or decrypt data using AES ECB.
 *
 * This API can be used to perform AES ECB on given data. The user will have to
 * pass a valid key reference in the form of a key_id to the API.
 *
 * @param[in]  sl_psa_key_id                        Key ID used as a reference to the key.
 * @param[in]  sl_psa_aes_alg                       AES Algorithm to use.
 * @param[in]  sl_psa_aes_input                     Pointer to the data to be encrypted or decrypted.
 * @param[out] sl_psa_aes_output                    Pointer to buffer to hold the output data.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_aes_encrypt(psa_key_id_t    sl_psa_key_id,
                                    psa_algorithm_t sl_psa_aes_alg,
                                    const uint8_t*  sl_psa_aes_input,
                                    uint8_t*        sl_psa_aes_output);

psa_status_t sl_sec_man_aes_decrypt(psa_key_id_t    sl_psa_key_id,
                                    psa_algorithm_t sl_psa_aes_alg,
                                    const uint8_t*  sl_psa_aes_input,
                                    uint8_t*        sl_psa_aes_output);

/**
 * API to encrypt data using AES-CCM*.
 *
 * This API can be used to perform AES-CCM* operations on given data which is in packet format.
 * (Authentication data is followed by the data to be encrypted/decrypted, with a MIC
 * located after the encryption data)
 * @note returns an error if given invalid psa key id
 *
 * @param[in]  sl_psa_key_id                        Key ID used as a reference to the key.
 * @param[in]  nonce                                Nonce value to use in the CCM* operation.
 * @param[in]  encrypt                              Function mode.  False for authenticated decryption,
                                                    true otherwise.
 * @param[in]  input                                Pointer to the packet to be encrypted.
 * @param[in]  encryption_start_index               Index where the data to be encrypted begins, equal to
 *                                                  the length of the authenticated data.
 * @param[in]  length                               Total combined length of authenticated and encrypted data.
 * @param[in]  mic_length                           Length of the MIC to be output after the encrypted data.
 *                                                  This must be consistent with what the referenced key
 *                                                  permits for its encryption algorithm.
 * @param[out] output                               Pointer to buffer to hold the output packet.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_aes_ccm_crypt(psa_key_id_t sl_psa_key_id,
                                      uint8_t* nonce,
                                      bool encrypt,
                                      const uint8_t* input,
                                      uint8_t encryption_start_index,
                                      uint8_t length,
                                      uint8_t mic_length,
                                      uint8_t* output);

/**
 * Start the HMAC operation.
 *
 * @param[in]   sl_psa_hmac_ctx                     Operation Context for HMAC operation.
 * @param[out]  sl_psa_key_id                       Reference to the key to be used for HMAC operation.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_hmac_start(psa_mac_operation_t *sl_psa_hmac_ctx, psa_key_id_t sl_psa_key_id);

/**
 * Update the HMAC operation with new input.
 *
 * @param[in]  sl_psa_hmac_ctx                      Operation Context for HMAC operation.
 * @param[out] sl_psa_hmac_buffer                   A pointer to the input buffer.
 * @param[in]  sl_psa_hmac_buffer_len               The length of @p sl_psa_hmac_buffer in bytes.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_hmac_update(psa_mac_operation_t *sl_psa_hmac_ctx,
                                    const uint8_t *      sl_psa_hmac_buffer,
                                    size_t               sl_psa_hmac_buffer_len);

/**
 * Complete the HMAC operation.
 *
 * @param[in]  sl_psa_hmac_ctx                      Operation Context for HMAC operation.
 * @param[out] sl_psa_hmac_buffer                   A pointer to the output buffer.
 * @param[in]  sl_psa_hmac_buffer_len               The length of @p sl_psa_hmac_buffer in bytes.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_hmac_finish(psa_mac_operation_t *sl_psa_hmac_ctx,
                                    const uint8_t *      sl_psa_hmac_buffer,
                                    size_t               sl_psa_hmac_buffer_len);

/**
 * Uninitialize the HMAC operation.
 *
 * @param[in]   sl_psa_hmac_ctx                     Operation Context for HMAC operation.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_hmac_deinit(psa_mac_operation_t *sl_psa_hmac_ctx);

/**
 * Perform HKDF Extract step.
 *
 * @param[in]  sl_psa_key_derivation_ctx            Operation context for key derivation operation.
 * @param[in]  sl_psa_key_derivation_algorithm      Algorithm being used for key derivation.
 * @param[in]  sl_psa_key_id                        Reference to key stored in PSA ITS.
 * @param[in]  sl_psa_key_derivation_salt           Pointer to the Salt for key derivation.
 * @param[in]  sl_psa_key_derivation_salt_length    Length of Salt.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_key_derivation_extract(psa_key_derivation_operation_t *sl_psa_key_derivation_ctx,
                                               psa_algorithm_t                 sl_psa_key_derivation_algorithm,
                                               psa_key_id_t                    sl_psa_key_id,
                                               const uint8_t *                 sl_psa_key_derivation_salt,
                                               uint16_t sl_psa_key_derivation_salt_length);

/**
 * Perform HKDF Expand step.
 *
 * @param[in]  sl_psa_key_derivation_ctx            Operation context for HKDF operation.
 * @param[in]  sl_psa_key_derivation_info           Pointer to the Info sequence.
 * @param[in]  sl_psa_key_derivation_info_length    Length of the Info sequence.
 * @param[out] sl_psa_key_derivation_output_key     Pointer to the output Key.
 * @param[in]  sl_psa_key_derivation_output_key_len Size of the output key buffer.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_key_derivation_expand(psa_key_derivation_operation_t *sl_psa_key_derivation_ctx,
                                              const uint8_t *                 sl_psa_key_derivation_info,
                                              uint16_t                        sl_psa_key_derivation_info_length,
                                              uint8_t *                       sl_psa_key_derivation_output_key,
                                              uint16_t sl_psa_key_derivation_output_key_len);

/**
 * Initialise the hashing operation.
 *
 * @param[in]  sl_psa_hash_ctx                      Context for hashing operation.
 *
 * @retval                                          Initial value for a hash operation object.
 */
psa_hash_operation_t sl_sec_man_hash_init(void);

/**
 * De-Initialise the hashing operation.
 *
 * @param[in]  sl_psa_hash_ctx                      Context for hashing operation.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 */
psa_status_t sl_sec_man_hash_deinit(psa_hash_operation_t *sl_psa_hash_ctx);

/**
 * Start SHA-256 operation.
 *
 * @param[in]  sl_psa_hash_ctx                      Context for hashing operation.
 * @param[in]  sl_psa_hash_alg                      Hashing algorithm to use.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_hash_start(psa_hash_operation_t *sl_psa_hash_ctx, psa_algorithm_t sl_psa_hash_alg);

/**
 * Update hashing operation with new input.
 *
 * @param[in]  sl_psa_hash_ctx                      Context for hashing operation.
 * @param[out] sl_psa_hash_buffer                   A pointer to the input buffer.
 * @param[in]  sl_psa_hash_buffer_len               The length of @p sl_psa_hash_buffer in bytes.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 */
psa_status_t sl_sec_man_hash_update(psa_hash_operation_t *sl_psa_hash_ctx,
                                    uint8_t *             sl_psa_hash_buffer,
                                    uint16_t              sl_psa_hash_buffer_len);

/**
 * Finish hashing operation.
 *
 * @param[in]  sl_psa_hash_ctx                      Context for hashing operation.
 * @param[in]  sl_psa_hash_hash                     A pointer to the output buffer, where hash needs to be stored.
 * @param[in]  sl_psa_hash_hash_size                The length of @p sl_psa_hash_hash in bytes.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 */
psa_status_t sl_sec_man_hash_finish(psa_hash_operation_t *sl_psa_hash_ctx,
                                    uint8_t *             sl_psa_hash,
                                    uint16_t              sl_psa_hash_size,
                                    size_t *              sl_psa_hash_len);

/**
 * Generate Entropy.
 *
 * @param[in]  sl_psa_output_buffer                 Buffer to store the generated entropy.
 * @param[in]  sl_psa_output_size                   Amount of entropy needed in bytes.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_get_random(uint8_t               *sl_psa_output_buffer,
                                   uint16_t              sl_psa_output_size);

/**
 * Export the public key from the key pair.
 *
 * @param[in]  sl_psa_key_id                        Key ID used as a reference to the key.
 * @param[out] sl_psa_output_buffer                 A pointer to the output buffer to store the public key.
 * @param[in]  sl_output_buffer_size                The length of @p sl_psa_output_buffer in bytes.
 * @param[out] sl_public_key_len                    A pointer to a variable to store length of the exported public key.
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_export_public_key(psa_key_id_t        sl_psa_key_id,
                                          uint8_t             *sl_psa_output_buffer,
                                          size_t              sl_output_buffer_size,
                                          size_t              *sl_public_key_len);

/**
 * Sign the given input buffer using the algorithm specified.
 *
 * @param[in]  sl_psa_key_id                        Key ID used as a reference to the key.
 * @param[in]  sl_dsa_algorithm                     DSA Algorithm to use to perform ECDSA sign operation.
 * @param[in]  sl_dsa_input_buf                     A pointer to the input buffer.
 * @param[in]  sl_dsa_input_size                    The length of @p sl_dsa_input_buf in bytes.
 * @param[out] sl_dsa_signature_buf                 A pointer to the output buffer to store the signature.
 * @param[in]  sl_dsa_signature_size                The length of @p sl_dsa_signature_buf in bytes.
 * @param[out] sl_dsa_signature_len                 A pointer to a variable to store length of the generated signature.
 * @param[in]  sl_is_hash                           Is the input a message hash?
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 *
 */
psa_status_t sl_sec_man_sign(psa_key_id_t        sl_psa_key_id,
                             psa_algorithm_t     sl_dsa_algorithm,
                             const uint8_t       *sl_dsa_input_buf,
                             size_t              sl_dsa_input_size,
                             uint8_t             *sl_dsa_signature_buf,
                             size_t              sl_dsa_signature_size,
                             size_t              *sl_dsa_signature_len,
                             bool                sl_is_hash);

/**
 * Verify the given signature using the algorithm specified.
 *
 * @param[in]  sl_psa_key_id                        Key ID used as a reference to the key.
 * @param[in]  sl_dsa_algorithm                     DSA Algorithm to use to perform ECDSA verify operation.
 * @param[in]  sl_dsa_input_buf                     A pointer to the input buffer.
 * @param[in]  sl_dsa_input_size                    The length of @p sl_dsa_input_buf in bytes.
 * @param[in]  sl_dsa_signature_buf                 A pointer to the input buffer to store the signature.
 * @param[in]  sl_dsa_signature_size                The length of @p sl_dsa_signature_buf in bytes.
 * @param[in]  sl_is_hash                           Is the input a message hash?
 *
 * @retval                                          A psa_status_t status code. Refer to /ref psa_status_t.
 */
psa_status_t sl_sec_man_verify(psa_key_id_t        sl_psa_key_id,
                               psa_algorithm_t     sl_dsa_algorithm,
                               const uint8_t       *sl_dsa_input_buf,
                               size_t              sl_dsa_input_size,
                               const uint8_t       *sl_dsa_signature_buf,
                               size_t              sl_dsa_signature_size,
                               bool                sl_is_hash);

#endif /* SECURITY_MANAGER_H_ */
