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

#ifndef SCP__SCP_CRYPT_H
#define SCP__SCP_CRYPT_H
#define FSP__SCP__SCP_CRYPT_H 1

/* Compiler headers */
#include <stdbool.h>                    // for bool
#include <stddef.h>                     // for size_t
#include <stdint.h>                     // for uint8_t, uintptr_t, etc.

/* Early FSP headers */
#include <misc/ct-assert.h>             // for HEADER_CHECK

/* Hardware headers */

/* Late FSP headers */
#include <error/common-errors.h>        // for error_t

/* Module-specific FSP headers */
#include <scp/scp_common.h>             // for scp_register_index_t

/*
 * Compile-time check for FSP header files
 *   Each FSP header file contains a signature unique to that file, and the
 *   FSP project.  The HEADER_CHECK macro (contained in misc/ct-assert.h) can
 *   check for this signature.  If it does not exist, then the build will
 *   abort.
 *
 *   This is a trap for projects which have their own include files of the
 *   same names, but different contents.  This trap ensures that only the
 *   files from the FSP project, are built into the FSP source code.
 */
HEADER_CHECK(FSP__ERROR__COMMON_ERRORS_H)
HEADER_CHECK(FSP__SCP__SCP_COMMON_H)


/**
 * @brief Constructs a new key-descriptor with automatic storage duration.
 *
 * When a descriptor returned by this macro is passed to scp_Encrypt(),
 * scp_Decrypt(), or scp_Cmac(), it instructs the callee to use the first
 * SCP_KEY_SIZE bytes from key_pa as the encryption key for the requested
 * operation.
 *
 * Note that the contents of key_pa are never copied or modified and must remain
 * valid for the lifetime of the returned key-descriptor.
 *
 * @param[in] key_pa The physical address of the input buffer containing the
 *                   desired raw key. Must be aligned to SCP_BUFFER_ALIGNMENT
 *                   and contain at least SCP_KEY_SIZE bytes. Supports IMEM and
 *                   DMEM locations only.
 *
 * @return A pointer to the new key-descriptor.
 */
#define SCP_KEY_RAW_BUFFER(key_pa) (              \
    (const scp_key_desc_t *)&(scp_key_desc_t)     \
    {                                             \
        .key_type      = SCP_KEY_TYPE_RAW_BUFFER, \
        .raw_buffer_pa = (uintptr_t)(key_pa),     \
    }                                             \
)

/**
 * @brief Constructs a new key-descriptor with automatic storage duration.
 *
 * When a descriptor returned by this macro is passed to scp_Encrypt(),
 * scp_Decrypt(), or scp_Cmac(), it instructs the callee to use the data located
 * in the register specified by reg_index as the encryption key for the
 * requested operation. This mode is intended to support keys pulled in from
 * KMEM.
 *
 * Note that the contents of the register will be cleared following use of the
 * returned key-descriptor in an encryption, decryption, or CMAC operation.
 * Additionally, the register's access-control list must permit keying of its
 * contents in secure contexts, or full access in insecure contexts.
 *
 * @param[in] reg_index The index of the SCP register containing the desired
 *                      raw key. Must be a valid scp_register_index_t value.
 *
 * @return A pointer to the new key-descriptor.
 */
#define SCP_KEY_RAW_REGISTER(reg_index) (                 \
    (const scp_key_desc_t *)&(scp_key_desc_t)             \
    {                                                     \
        .key_type           = SCP_KEY_TYPE_RAW_REGISTER,  \
        .raw_register_index = (reg_index),                \
    }                                                     \
)

/**
 * @brief Constructs a new key-descriptor with automatic storage duration.
 *
 * When a descriptor returned by this macro is passed to scp_Encrypt(),
 * scp_Decrypt(), or scp_Cmac(), it instructs the callee to perform AES-ECB
 * encryption on the first SCP_BLOCK_SIZE bytes from salt_pa, using the
 * hardware-secret indicated by secret_index as the key. The result of this
 * procedure is then used as the encryption key for the requested encryption,
 * decryption, or CMAC operation.
 *
 * Note that the contents of salt_pa are never copied or modified and must
 * remain valid for the lifetime of the returned key-descriptor. Additionally,
 * the hardware-secret referred to by secret_index must have an access-control
 * list that permits keying of its contents for the current operating context
 * (secure or insecure).
 *
 * @param[in] secret_index  The index of the hardware-secret to be used in the
 *                          key-derivation process. Must be a positive integer
 *                          less than SCP_SECRET_COUNT.
 *
 * @param[in] salt_pa       The physical address of the input buffer containing
 *                          the salt value to be used in the key-derivation
 *                          process. Must be aligned to SCP_BUFFER_ALIGNMENT
 *                          and contain at least SCP_BLOCK_SIZE bytes. Supports
 *                          IMEM and DMEM locations only.
 *
 * @return A pointer to the new key-descriptor.
 */
#define SCP_KEY_DERIVED_ECB(secret_index, salt_pa) (   \
    (const scp_key_desc_t *)&(scp_key_desc_t)          \
    {                                                  \
        .key_type         = SCP_KEY_TYPE_DERIVED_ECB,  \
        .ecb_secret_index = (secret_index),            \
        .ecb_salt_pa      = (uintptr_t)(salt_pa),      \
    }                                                  \
)

/**
 * @brief Constructs a new key-descriptor with automatic storage duration.
 *
 * When a descriptor returned by this macro is passed to scp_Encrypt(),
 * scp_Decrypt(), or scp_Cmac(), it instructs the callee to perform AES-CBC
 * encryption on the first SCP_BLOCK_SIZE bytes from salt_pa, using the
 * hardware-secret indicated by secret_index as the key and the first
 * SCP_BLOCK_SIZE bytes from iv_pa as the initialization vector. The result of
 * this procedure is then used as the encryption key for the requested
 * encryption, decryption, or CMAC operation.
 *
 * Note that the contents of salt_pa and iv_pa are never copied or modified and
 * must remain valid for the lifetime of the returned key-descriptor.
 * Additionally, the hardware-secret referred to by secret_index must have an
 * access-control list that permits keying of its contents for the current
 * operating context (secure or insecure).
 *
 * @param[in] secret_index  The index of the hardware-secret to be used in the
 *                          key-derivation process. Must be a positive integer
 *                          less than SCP_SECRET_COUNT.
 *
 * @param[in] salt_pa       The physical address of the input buffer containing
 *                          the salt value to be used in the key-derivation
 *                          process. Must be aligned to SCP_BUFFER_ALIGNMENT
 *                          and contain at least SCP_BLOCK_SIZE bytes. Supports
 *                          IMEM and DMEM locations only.
 *
 * @param[in] iv_pa         The physical address of the input buffer containing
 *                          the initialization vector to be used in the key-
 *                          derivation process. Must be aligned to
 *                          SCP_BUFFER_ALIGNMENT and contain at least
 *                          SCP_BLOCK_SIZE bytes. Supports IMEM and DMEM
 *                          locations only.
 *
 * @return A pointer to the new key-descriptor.
 */
#define SCP_KEY_DERIVED_CBC(secret_index, salt_pa, iv_pa) ( \
    (const scp_key_desc_t *)&(scp_key_desc_t)               \
    {                                                       \
        .key_type         = SCP_KEY_TYPE_DERIVED_CBC,       \
        .cbc_secret_index = (secret_index),                 \
        .cbc_salt_pa      = (uintptr_t)(salt_pa),           \
        .cbc_iv_pa        = (uintptr_t)(iv_pa),             \
    }                                                       \
)

/**
 * @brief Constructs a new key-descriptor with automatic storage duration.
 *
 * When a descriptor returned by this macro is passed to scpEncrypt(),
 * scpDecrypt(), or scpCmac(), it instructs the callee to compute the CMAC of
 * the first size bytes from message_pa, using the hardware-secret indicated by
 * secret_index as the key. The result of this procedure is then used as the
 * encryption key for the requested encryption, decryption, or CMAC operation.
 *
 * Per NIST SP 800-108, it is assumed that message_pa contains the requisite
 * counter, label, context, and length values in the form of a pre-concatenated
 * bit string. In the event that this string is not a multiple of SCP_BLOCK_SIZE
 * bytes in length, it is further assumed that the application has applied
 * padding per the CMAC specification (i.e. by appending 0b10...0) and has
 * signalled this padding by setting b_padded to true.
 *
 * Note that the contents of message_pa are never copied or modified and must
 * remain valid for the lifetime of the returned key-descriptor. Additionally,
 * the hardware-secret referred to by secret_index must have an access-control
 * list that permits keying of its contents for the current operating context
 * (secure or insecure).
 *
 * @param[in] secret_index  The index of the hardware-secret to be used in the
 *                          key-derivation process. Must be a positive integer
 *                          less than SCP_SECRET_COUNT.
 *
 * @param[in] message_pa    The physical address of the input buffer containing
 *                          the message to be used in the key-derivation
 *                          process. Must be aligned to SCP_BUFFER_ALIGNMENT
 *                          and contain at least size bytes. Supports IMEM and
 *                          DMEM locations only.
 *
 * @param[in] size          The number of bytes to process from message_pa. Must
 *                          be a nonzero multiple of SCP_BLOCK_SIZE.
 *
 * @param[in] b_padded      A boolean value indicating whether the source
 *                          message contains padding.
 *
 * @return A pointer to the new key-descriptor.
 */
#define SCP_KEY_DERIVED_CMAC(secret_index, message_pa, size, b_padded) (    \
    (const scp_key_desc_t *)&(scp_key_desc_t)                               \
    {                                                                       \
        .key_type           = SCP_KEY_TYPE_DERIVED_CMAC,                    \
        .cmac_secret_index  = (secret_index),                               \
        .cmac_message_pa    = (uintptr_t)(message_pa),                      \
        .cmac_size          = (size),                                       \
        .cmac_padded        = (b_padded),                                   \
    }                                                                       \
)

/**
 * @brief The cryptographic block size.
 *
 * The size, in bytes, of a single block of data in the context of
 * cryptographic operations.
 */
#define SCP_BLOCK_SIZE 16U

/**
 * @brief The cryptographic key size.
 *
 * The size, in bytes, of an encryption key in the context of cryptographic
 * operations.
 */
#define SCP_KEY_SIZE 16U

/**
 * @brief The number of hardware secrets supported by the SCP unit.
 */
#define SCP_SECRET_COUNT 64U


/**
 * @brief A collection of supported block-cipher modes.
 */
typedef enum scp_cipher_mode_t
{
    SCP_CIPHER_MODE_ECB,
    SCP_CIPHER_MODE_CBC,
    SCP_CIPHER_MODE_CTR,
} scp_cipher_mode_t;

/**
 * @brief A collection of supported operating modes for CMAC computations.
 */
typedef enum scp_cmac_mode_t
{
    SCP_CMAC_MODE_COMPLETE,
    SCP_CMAC_MODE_PADDED,
    SCP_CMAC_MODE_PARTIAL,
} scp_cmac_mode_t;

/**
 * @brief A structure for describing cryptographic keys.
 *
 * A structure for communicating key information to applicable cryptographic
 * interfaces. Should always be constructed using the provided SCP_KEY_* helper
 * macros.
 *
 * @key_type            The type of key expressed by this key-descriptor.
 *
 * @raw_buffer_pa       The physical address of the source buffer containing
 *                      the key (when key_type is SCP_KEY_TYPE_RAW_BUFFER).
 *
 * @raw_register_index  The index of the source register containing the key
 *                      (when key_type is SCP_KEY_TYPE_RAW_REGISTER).
 *
 * @ecb_secret_index    The hardware secret to be used in the key-derivation
 *                      process (when key_type is SCP_KEY_TYPE_DERIVED_ECB).
 *
 * @ecb_salt_pa         The physical address of the salt value to derive from
 *                      (when key_type is SCP_KEY_TYPE_DERIVED_ECB).
 *
 * @cbc_secret_index    The hardware secret to be used in the key-derivation
 *                      process (when key_type is SCP_KEY_TYPE_DERIVED_CBC).
 *
 * @cbc_salt_pa         The physical address of the salt value to derive from
 *                      (when key_type is SCP_KEY_TYPE_DERIVED_CBC).
 *
 * @cbc_iv_pa           The physical address of the initialization vector for
 *                      CBC (when key_type is SCP_KEY_TYPE_DERIVED_CBC).
 *
 * @cmac_secret_index   The hardware secret to be used in the key-derivation
 *                      process (when key_type is SCP_KEY_TYPE_DERIVED_CMAC).
 *
 * @cmac_message_pa     The physical address of the message to derive from
 *                      (when key_type is SCP_KEY_TYPE_DERIVED_CMAC).
 *
 * @cmac_size           The size of the message to derive from (when key_type is
 *                      SCP_KEY_TYPE_DERIVED_CMAC).
 *
 * @cmac_padded         Whether the input message contains padding (when
 *                      key_type is SCP_KEY_TYPE_DERIVED_CMAC).
 */
typedef struct scp_key_desc_t
{
    /**
     * @brief A collection of supported key types.
     *
     * See the corresponding SCP_KEY_* helper macros for more information
     * regarding the different supported key types.
     */
    enum scp_key_type_t
    {
        SCP_KEY_TYPE_RAW_BUFFER,
        SCP_KEY_TYPE_RAW_REGISTER,

        SCP_KEY_TYPE_DERIVED_ECB,
        SCP_KEY_TYPE_DERIVED_CBC,
        SCP_KEY_TYPE_DERIVED_CMAC,
    } key_type;

    // Type-specific key information.
    union
    {
        // Information for RAW_BUFFER-type keys.
        struct
        {
            uintptr_t               raw_buffer_pa;
        };

        // Information for RAW_REGISTER-type keys.
        struct
        {
            scp_register_index_t    raw_register_index;
        };

        // Information for DERIVED_ECB-type keys.
        struct
        {
            uint8_t                 ecb_secret_index;
            uintptr_t               ecb_salt_pa;
        };

        // Information for DERIVED_CBC-type keys.
        struct
        {
            uint8_t                 cbc_secret_index;
            uintptr_t               cbc_salt_pa;
            uintptr_t               cbc_iv_pa;
        };

        // Information for DERIVED_CMAC-type keys.
        struct
        {
            uint8_t                 cmac_secret_index;
            uintptr_t               cmac_message_pa;
            size_t                  cmac_size;
            bool                    cmac_padded;
        };
    };
} scp_key_desc_t;


/**
 * @brief Encrypts an arbitary buffer of data using an AES-128 block cipher.
 *
 * Encrypts the first size bytes of data from source_pa using the key described
 * by p_key_desc and writes the result to dest_pa. Uses an AES-128 block cipher
 * operating in the mode indicated by cipher_mode as the encryption algorithm.
 * If applicable, SCP_BLOCK_SIZE bytes from context_pa are used as the initial
 * contextual input to the cipher.
 *
 * Should p_key_desc describe a derived key, the scp_Encrypt() function will
 * perform the indicated key-derivation operation automatically. See the
 * relevant SCP_KEY_* macros for details.
 *
 * Note that it is permissible for source_pa and dest_pa to reference the same
 * memory location (i.e. to facilitate encrypting data in-place). However, the
 * use of buffers that only partially overlap is prohibited and will result in
 * an error.
 *
 * In the event that dest_pa refers to an external memory location, the
 * application must ensure that it has first called scp_Configure_Shortcut() (or
 * used the SCP_EXTERNAL_DEST() helper macro) to configure the shortcut-DMA
 * path accordingly.
 *
 * @param[in]  p_key_desc   A pointer to the key-descriptor to be used for the
 *                          encryption operation. Applications should acquire
 *                          this parameter through one of the provided
 *                          SCP_KEY_* macros.
 *
 * @param[in]  cipher_mode  The block-cipher mode of operation to use. See the
 *                          documentation for scp_cipher_mode_t for a list of
 *                          supported values.
 *
 * @param[in]  source_pa    The physical address of the input buffer to be
 *                          encrypted. Must be aligned to SCP_BUFFER_ALIGNMENT
 *                          and contain at least size bytes. Supports IMEM and
 *                          DMEM locations only.
 *
 * @param[out] dest_pa      The physical address of the output buffer to which
 *                          the encrypted data is to be written. Must be
 *                          aligned to SCP_BUFFER_ALIGNMENT and have at least
 *                          size bytes of capacity. Permitted to overlap with
 *                          source_pa only if exactly equal. Supports IMEM,
 *                          DMEM, and external locations.
 *
 * @param[in]  size         The number of bytes to process from source_pa. Must
 *                          be a nonzero multiple of SCP_BLOCK_SIZE.
 *
 * @param[in]  context_pa   The physical address of the input buffer containing
 *                          the initialization vector (CBC) or initial counter
 *                          value with nonce (CTR) to be used for the
 *                          encryption operation. Must be aligned to
 *                          SCP_BUFFER_ALIGNMENT and contain at least
 *                          SCP_BLOCK_SIZE bytes. Required to be zero (NULL)
 *                          for ECB mode. Supports IMEM and DMEM locations
 *                          only.
 *
 * @retval E_SUCCESS                    if data was encrypted successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INVALID_OBJECT         if p_key_desc does not point to a valid
 *                                      key-descriptor.
 *
 * @retval E_SCP_INVALID_OBJECT_BUFFER  if p_key_desc was constructed with a
 *                                      zero (NULL) or incorrectly-aligned
 *                                      address, or a pointer to an unsupported
 *                                      memory location (e.g. EMEM), or a buffer
 *                                      that overflows the memory region in
 *                                      which it resides, or a buffer of invalid
 *                                      size.
 *
 * @retval E_SCP_INVALID_INDEX          if p_key_desc was constructed with an
 *                                      invalid register or hardware-secret
 *                                      index, or references secret-index zero.
 *
 * @retval E_PERM                       if p_key_desc was constructed with a
 *                                      hardware-secret index that the
 *                                      application does not have permission to
 *                                      use.
 *
 * @retval E_SCP_INVALID_POINTER        if one or more of p_key_desc, source_pa,
 *                                      or dest_pa is zero/NULL, or context_pa
 *                                      is zero (NULL) and cipher_mode is not
 *                                      SCP_CIPHER_MODE_ECB.
 *
 * @retval E_SCP_INVALID_ADDRESS        if one or more of source_pa, dest_pa, or
 *                                      context_pa is incorrectly aligned, or
 *                                      the buffers referenced by source_pa and
 *                                      dest_pa overlap without being equal.
 *
 * @retval E_SCP_INVALID_BASE           if one or more of source_pa, dest_pa, or
 *                                      context_pa points to an unsupported
 *                                      memory region (e.g. EMEM).
 *
 * @retval E_SCP_INVALID_REQUEST        if size is zero or is not a multiple of
 *                                      SCP_BLOCK_SIZE.
 *
 * @retval E_SCP_INVALID_OPERATION      if cipher_mode is not a valid
 *                                      scp_cipher_mode_t value, or cipher_mode
 *                                      is SCP_CIPHER_MODE_ECB and context_pa is
 *                                      nonzero (non-NULL).
 *
 * @retval E_SCP_FEATURE_NOT_ENABLED    if dest_pa is located in external memory
 *                                      but the application does not have
 *                                      shortcut-DMA support enabled.
 *
 * @retval E_SCP_INVALID_DMA_SPECIFIER  if dest_pa is located in external memory
 *                                      but the application has not called
 *                                      scp_Configure_Shortcut() to configure
 *                                      the shortcut-DMA path.
 *
 * @retval E_SCP_OUT_OF_RANGE           if one or more of the buffers referenced
 *                                      by source_pa, dest_pa, or context_pa
 *                                      overflows the memory region in which it
 *                                      resides.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_INVALID_STATE              if calls to this function are prohibited
 *                                      because the SCP driver is operating in
 *                                      direct mode.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown.
 *
 * @retval E_FAULT                      if an IO-PMP fault was detected.
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Encrypt(const scp_key_desc_t * p_key_desc,
                    scp_cipher_mode_t      cipher_mode,
                    uintptr_t              source_pa,
                    uintptr_t              dest_pa,
                    size_t                 size,
                    uintptr_t              context_pa);

/**
 * @brief Decrypts an arbitary buffer of data using an AES-128 block cipher.
 *
 * Decrypts the first size bytes of data from source_pa using the key described
 * by p_key_desc and writes the result to dest_pa. Uses an AES-128 block cipher
 * operating in the mode indicated by cipher_mode as the decryption algorithm.
 * If applicable, SCP_BLOCK_SIZE bytes from context_pa are used as the initial
 * contextual input to the cipher.
 *
 * Should p_key_desc describe a derived key, the scp_Decrypt() function will
 * perform the indicated key-derivation operation automatically. See the
 * relevant SCP_KEY_* macros for details.
 *
 * Note that it is permissible for source_pa and dest_pa to reference the same
 * memory location (i.e. to facilitate decrypting data in-place). However, the
 * use of buffers that only partially overlap is prohibited and will result in
 * an error.
 *
 * In the event that dest_pa refers to an external memory location, the
 * application must ensure that it has first called scp_Configure_Shortcut() (or
 * used the SCP_EXTERNAL_DEST() helper macro) to configure the shortcut-DMA
 * path accordingly.
 *
 * @param[in]  p_key_desc   A pointer to the key-descriptor to be used for the
 *                          decryption operation. Applications should acquire
 *                          this parameter through one of the provided
 *                          SCP_KEY_* macros.
 *
 * @param[in]  cipher_mode  The block-cipher mode of operation to use. See the
 *                          documentation for scp_cipher_mode_t for a list of
 *                          supported values.
 *
 * @param[in]  source_pa    The physical address of the input buffer to be
 *                          decrypted. Must be aligned to SCP_BUFFER_ALIGNMENT
 *                          and contain at least size bytes. Supports IMEM and
 *                          DMEM locations only.
 *
 * @param[out] dest_pa      The physical address of the output buffer to which
 *                          the decrypted data is to be written. Must be
 *                          aligned to SCP_BUFFER_ALIGNMENT and have at least
 *                          size bytes of capacity. Permitted to overlap with
 *                          source_pa only if exactly equal. Supports IMEM,
 *                          DMEM, and external locations.
 *
 * @param[in]  size         The number of bytes to process from source_pa. Must
 *                          be a nonzero multiple of SCP_BLOCK_SIZE.
 *
 * @param[in]  context_pa   The physical address of the input buffer containing
 *                          the initialization vector (CBC) or initial counter
 *                          value with nonce (CTR) to be used for the
 *                          decryption operation. Must be aligned to
 *                          SCP_BUFFER_ALIGNMENT and contain at least
 *                          SCP_BLOCK_SIZE bytes. Required to be zero (NULL)
 *                          for ECB mode. Supports IMEM and DMEM locations
 *                          only.
 *
 * @retval E_SUCCESS                    if data was decrypted successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INVALID_OBJECT         if p_key_desc does not point to a valid
 *                                      key-descriptor.
 *
 * @retval E_SCP_INVALID_OBJECT_BUFFER  if p_key_desc was constructed with a
 *                                      zero (NULL) or incorrectly-aligned
 *                                      address, or a pointer to an unsupported
 *                                      memory location (e.g. EMEM), or a buffer
 *                                      that overflows the memory region in
 *                                      which it resides, or a buffer of invalid
 *                                      size.
 *
 * @retval E_SCP_INVALID_INDEX          if p_key_desc was constructed with an
 *                                      invalid register or hardware-secret
 *                                      index, or references secret-index zero.
 *
 * @retval E_PERM                       if p_key_desc was constructed with a
 *                                      hardware-secret index that the
 *                                      application does not have permission to
 *                                      use.
 *
 * @retval E_SCP_INVALID_POINTER        if one or more of p_key_desc, source_pa,
 *                                      or dest_pa is zero/NULL, or context_pa
 *                                      is zero (NULL) and cipher_mode is not
 *                                      SCP_CIPHER_MODE_ECB.
 *
 * @retval E_SCP_INVALID_ADDRESS        if one or more of source_pa, dest_pa, or
 *                                      context_pa is incorrectly aligned, or
 *                                      the buffers referenced by source_pa and
 *                                      dest_pa overlap without being equal.
 *
 * @retval E_SCP_INVALID_BASE           if one or more of source_pa, dest_pa, or
 *                                      context_pa points to an unsupported
 *                                      memory region (e.g. EMEM).
 *
 * @retval E_SCP_INVALID_REQUEST        if size is zero or is not a multiple of
 *                                      SCP_BLOCK_SIZE.
 *
 * @retval E_SCP_INVALID_OPERATION      if cipher_mode is not a valid
 *                                      scp_cipher_mode_t value, or cipher_mode
 *                                      is SCP_CIPHER_MODE_ECB and context_pa is
 *                                      nonzero (non-NULL).
 *
 * @retval E_SCP_FEATURE_NOT_ENABLED    if dest_pa is located in external memory
 *                                      but the application does not have
 *                                      shortcut-DMA support enabled.
 *
 * @retval E_SCP_INVALID_DMA_SPECIFIER  if dest_pa is located in external memory
 *                                      but the application has not called
 *                                      scp_Configure_Shortcut() to configure
 *                                      the shortcut-DMA path.
 *
 * @retval E_SCP_OUT_OF_RANGE           if one or more of the buffers referenced
 *                                      by source_pa, dest_pa, or context_pa
 *                                      overflows the memory region in which it
 *                                      resides.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_INVALID_STATE              if calls to this function are prohibited
 *                                      because the SCP driver is operating in
 *                                      direct mode.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown.
 *
 * @retval E_FAULT                      if an IO-PMP fault was detected.
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Decrypt(const scp_key_desc_t * p_key_desc,
                    scp_cipher_mode_t      cipher_mode,
                    uintptr_t              source_pa,
                    uintptr_t              dest_pa,
                    size_t                 size,
                    uintptr_t              context_pa);

/**
 * @brief Computes the Davies-Meyer hash of an arbitrary buffer of data.
 *
 * Computes the Davies-Meyer hash of the first size bytes of data from source_pa
 * and writes the result to dest_pa. Uses SCP_BLOCK_SIZE bytes from init_pa as
 * the initial hash value.
 *
 * Note that it is permissible for dest_pa and init_pa to alias to the same
 * memory location.
 *
 * In the event that dest_pa refers to an external memory location, the
 * application must ensure that it has first called scp_Configure_Shortcut() (or
 * used the SCP_EXTERNAL_DEST() helper macro) to configure the shortcut-DMA
 * path accordingly.
 *
 * @param[in]  source_pa The physical address of the input buffer whose hash
 *                       will be computed. Must be aligned to
 *                       SCP_BUFFER_ALIGNMENT and contain at least size bytes.
 *                       Supports IMEM and DMEM locations only.
 *
 * @param[in]  size      The number of bytes to process from source_pa. Must be
 *                       a nonzero multiple of SCP_KEY_SIZE.
 *
 * @param[out] dest_pa   The physical address of the output buffer to which the
 *                       computed hash value is to be written. Must be aligned
 *                       to SCP_BUFFER_ALIGNMENT and have at least
 *                       SCP_BLOCK_SIZE bytes of capacity. Supports IMEM, DMEM,
 *                       and external locations.
 *
 * @param[in]  init_pa   The physical address of the input buffer containing the
 *                       initial hash value H0 to be used in the computation.
 *                       Must be aligned to SCP_BUFFER_ALIGNMENT and contain at
 *                       least SCP_BLOCK_SIZE bytes. Permitted to alias to
 *                       dest_pa. Supports IMEM and DMEM locations only.
 *
 * @retval E_SUCCESS                    if hash was computed successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retcal E_SCP_INVALID_POINTER        if one or more of source_pa, dest_pa, or
 *                                      init_pa is zero/NULL.
 *
 * @retval E_SCP_INVALID_ADDRESS        if one or more of source_pa, dest_pa, or
 *                                      init_pa is incorrectly aligned.
 *
 * @retval E_SCP_INVALID_BASE           if one or more of source_pa, dest_pa, or
 *                                      init_pa points to an unsupported memory
 *                                      location (e.g. EMEM).
 *
 * @retval E_SCP_INVALID_REQUEST        if size is zero or is not a multiple of
 *                                      SCP_KEY_SIZE.
 *
 * @retval E_SCP_FEATURE_NOT_ENABLED    if dest_pa is located in external memory
 *                                      but the application does not have
 *                                      shortcut-DMA support enabled.
 *
 * @retval E_SCP_INVALID_DMA_SPECIFIER  if dest_pa is located in external memory
 *                                      but the application has not called
 *                                      scp_Configure_Shortcut() to configure
 *                                      the shortcut-DMA path.
 *
 * @retval E_SCP_OUT_OF_RANGE           if one or more of the buffers referenced
 *                                      by source_pa, dest_pa, or init_pa
 *                                      overflows the memory region in which it
 *                                      resides.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_INVALID_STATE              if calls to this function are prohibited
 *                                      because the SCP driver is operating in
 *                                      direct mode.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown.
 *
 * @retval E_FAULT                      if an IO-PMP fault was detected.
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Dmhash(uintptr_t source_pa,
                   size_t    size,
                   uintptr_t dest_pa,
                   uintptr_t init_pa);

/**
 * @brief Computes the CMAC of an arbitrary buffer of data.
 *
 * Computes the CMAC of the first size bytes of data from source_pa using the
 * key described by p_key_desc and writes the result to dest_pa. Uses an AES-128
 * block cipher for the operation. If provided, SCP_BLOCK_SIZE bytes from
 * context_pa are used as the initial contextual input (IV) for the computation.
 *
 * When cmac_mode is SCP_CMAC_MODE_PARTIAL, scp_Cmac() skips the final step of
 * the CMAC computation, thus allowing the result to be chained into a future
 * call via context_pa. The full CMAC result can then be obtained by passing
 * SCP_CMAC_MODE_COMPLETE, as is done for singular, unchained calls.
 *
 * Notice that it is the application's responsibility to pad the source message
 * if needed (by appending 0b10...0 per the CMAC specification) and to signal
 * that padding has occurred by setting cmac_mode to SCP_CMAC_MODE_PADDED in
 * place of SCP_CMAC_MODE_COMPLETE.
 *
 * Should p_key_desc describe a derived key, the scp_Cmac() function will
 * perform the indicated key-derivation operation automatically. See the
 * relevant SCP_KEY_* macros for details.
 *
 * Note that it is permissible for dest_pa and context_pa to reference the same
 * memory location (e.g. for easy chaining of partial computations - see
 * above).
 *
 * In the event that dest_pa refers to an external memory location, the
 * application must ensure that it has first called scp_Configure_Shortcut() (or
 * used the SCP_EXTERNAL_DEST() helper macro) to configure the shortcut-DMA
 * path accordingly.
 *
 * @param[in]  p_key_desc   A pointer to the key-descriptor to be used for the
 *                          CMAC computation. Applications should acquire this
 *                          parameter through one of the provided SCP_KEY_*
 *                          macros.
 *
 * @param[in]  cmac_mode    The CMAC mode of operation to use. See the
 *                          documentation for scp_cmac_mode_t for a list of
 *                          supported values.
 *
 * @param[in]  source_pa    The physical address of the input buffer for which
 *                          the CMAC will be computed. Must be aligned to
 *                          SCP_BUFFER_ALIGNMENT and contain at least size
 *                          bytes. Supports IMEM and DMEM locations only.
 *
 * @param[in]  size         The number of bytes to process from source_pa. Must
 *                          be a nonzero multiple of SCP_BLOCK_SIZE.
 *
 * @param[out] dest_pa      The physical address of the output buffer to which
 *                          the computed CMAC value is to be written. Must be
 *                          aligned to SCP_BUFFER_ALIGNMENT and have at least
 *                          SCP_BLOCK_SIZE bytes of capacity. Supports IMEM,
 *                          DMEM, and external locations.
 *
 * @param[in]  context_pa   The physical address of the input buffer containing
 *                          the result of a prior partial CMAC computation, if
 *                          applicable. Must be aligned to SCP_BUFFER_ALIGNMENT
 *                          and contain at least SCP_BLOCK_SIZE bytes. Should
 *                          be zero (NULL) or should point to an all-zero
 *                          buffer if not continuing a prior computation.
 *                          Permitted to alias to dest_pa. Supports IMEM and
 *                          DMEM locations only.
 *
 * @retval E_SUCCESS                    if CMAC was computed successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INVALID_OBJECT         if p_key_desc does not point to a valid
 *                                      key-descriptor.
 *
 * @retval E_SCP_INVALID_OBJECT_BUFFER  if p_key_desc was constructed with a
 *                                      zero (NULL) or incorrectly-aligned
 *                                      address, or a pointer to an unsupported
 *                                      memory location (e.g. EMEM), or a buffer
 *                                      that overflows the memory region in
 *                                      which it resides, or a buffer of invalid
 *                                      size.
 *
 * @retval E_SCP_INVALID_INDEX          if p_key_desc was constructed with an
 *                                      invalid register or hardware-secret
 *                                      index, or references secret-index zero.
 *
 * @retval E_PERM                       if p_key_desc was constructed with a
 *                                      hardware-secret index that the
 *                                      application does not have permission to
 *                                      use.
 *
 * @retval E_SCP_INVALID_POINTER        if one or more of p_key_desc, source_pa,
 *                                      or dest_pa is zero/NULL.
 *
 * @retval E_SCP_INVALID_ADDRESS        if one or more of source_pa, dest_pa, or
 *                                      context_pa is incorrectly aligned, or
 *                                      the buffers referenced by source_pa and
 *                                      context_pa overlap.
 *
 * @retval E_SCP_INVALID_BASE           if one or more of source_pa, dest_pa, or
 *                                      context_pa points to an unsupported
 *                                      memory region (e.g. EMEM).
 *
 * @retval E_SCP_INVALID_REQUEST        if size is zero or is not a multiple of
 *                                      SCP_BLOCK_SIZE.
 *
 * @retval E_SCP_INVALID_OPERATION      if cmac_mode is not a valid
 *                                      scp_cmac_mode_t value.
 *
 * @retval E_SCP_FEATURE_NOT_ENABLED    if dest_pa is located in external memory
 *                                      but the application does not have
 *                                      shortcut-DMA support enabled.
 *
 * @retval E_SCP_INVALID_DMA_SPECIFIER  if dest_pa is located in external memory
 *                                      but the application has not called
 *                                      scp_Configure_Shortcut() to configure
 *                                      the shortcut-DMA path.
 *
 * @retval E_SCP_OUT_OF_RANGE           if one or more of the buffers referenced
 *                                      by source_pa, dest_pa, or context_pa
 *                                      overflows the memory region in which it
 *                                      resides.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_INVALID_STATE              if calls to this function are prohibited
 *                                      because the SCP driver is operating in
 *                                      direct mode.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown.
 *
 * @retval E_FAULT                      if an IO-PMP fault was detected.
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Cmac(const scp_key_desc_t * p_key_desc,
                 scp_cmac_mode_t        cmac_mode,
                 uintptr_t              source_pa,
                 size_t                 size,
                 uintptr_t              dest_pa,
                 uintptr_t              context_pa);

#endif // SCP__SCP_CRYPT_H
