MTB CAT1 Peripheral driver library
Client-Server Model

General Description

Use the client-server API to isolate the Crypto hardware from non-secure application access.

The functions and other declarations used in this part of the driver are in cy_crypto.h and cy_crypto_server.h. You can also include cy_pdl.h to get access to all functions and declarations in the PDL.

The firmware initializes and starts the Crypto server. The server can run on any core and works with the Crypto hardware. The Crypto server is implemented as a secure block. It performs all cryptographic operations for the client. Access to the server is through the Inter Process Communication (IPC) driver. Direct access is not allowed.

The Crypto client can run on any core too. The firmware initializes and starts the client. The firmware then provides configuration data required for the desired cryptographic technique and a request that the server run the cryptographic operation.

This document contains the following topics:

Architectural model

The client-server implementation uses:

Firmware initializes and starts the Crypto server. The server can run on any core and works with the Crypto hardware. The Crypto server is implemented as a secure block. It performs all cryptographic operations for the client. Access to the server is through the Inter Process Communication (IPC) driver. Direct access is not allowed.

The Crypto client can also run on any core. Firmware initializes and starts the client. The firmware then provides the configuration data required for the desired cryptographic technique, and requests that the server run the cryptographic operation.

Note
Only one Crypto server and only one Crypto client can be run at the same time on any core. So, valid configurations are:
  • one server instance runs on CM0+ and one client instance on CM4 (and vice versa).
  • one server instance and one client instance run on CM0+ (** This is not recommended, use Direct Crypto API's to perform Crypto operations on a single core).
  • one server instance and one client instance run on CM4 (** This is not recommended, use Direct Crypto API's to perform Crypto operations on a single core).
crypto_architecture.png

IPC communication between the client and server is completely transparent. Using IPC for communication provides a simple synchronization mechanism to handle concurrent requests from different cores.

Configuration Structure

IPC communication for the Crypto driver is handled transparently. User should select the IPC channel, and configure the required notify, release, and error interrupts.

These initialization routines, Cy_Crypto_Server_Start_Base, Cy_Crypto_Server_Start_Extra or Cy_Crypto_Server_Start_Full (server) and Cy_Crypto_Init (client), use separate instances of the same cy_stc_crypto_config_t configuration structure. Some fields should be the same, and some are set specifically by either the server or client. The table lists each field in the config structure, and which initialization routine sets the value.

FieldWhichDescriptionNotes
ipcChannel Server and Client IPC channel for communication between client and server IPC Channel, same for both
acquireNotifierChannel Server and Client IPC interrupt structure used for the new request notifications Notify interrupt number, for Server side only
releaseNotifierChannel Server and Client IPC interrupt structure used for data ready notifications. Used to call userCompleteCallback handler function. Release interrupt number, for Client side only
userCompleteCallback Client User-defined callback for the Release interrupt handler; can be NULL See Implementing Crypto Interrupts
releaseNotifierConfig Client IRQ handler settings for data ready notifications. This interrupt occurs when server completely processed all input data and released an IPC communication channel. configuration for the interrupt
userGetDataHandler Server User-defined function to override default interrupt handler; NULL = use default ISR for the Notify interrupt
acquireNotifierConfig Server IRQ handler settings for new request notifications. This interrupt occurs when client sent a new request for processing. configuration for the interrupt
userErrorHandler Server User-defined function to override default interrupt handler; NULL = use default ISR for a server error
cryptoErrorIntrConfig Server IRQ handler settings for hardware error events configuration for the interrupt

Server Initialization

Use a Crypto Server Start function (one of Cy_Crypto_Server_Start_Base, Cy_Crypto_Server_Start_Extra or Cy_Crypto_Server_Start_Full). Provide the configuration parameters (cy_stc_crypto_config_t) and a pointer to the server context (cy_stc_crypto_server_context_t) that will be used to store all temporary data.

/* Scenario: Configure Server and Client as follows:
* Server:
* - IPC channel for communication: 13
* - IPC interrupt structure used for new request notifications: 13
* - Data request handler: default
* - Hardware errors handler: default
* Client:
* - IPC channel for communication: 13
* - IPC interrupt structure used for data ready notifications: 14
* - Data Complete callback: not used
*/
#define MY_CHAN_CRYPTO (uint32_t)(13u) /* IPC data channel for the Crypto */
#define MY_INTR_CRYPTO_SRV (uint32_t)(13u) /* IPC interrupt structure for the Crypto server */
#define MY_INTR_CRYPTO_CLI (uint32_t)(14u) /* IPC interrupt structure for the Crypto client */
#define MY_INTR_CRYPTO_SRV_MUX (IRQn_Type)(2u) /* CM0+ IPC interrupt mux number the Crypto server */
#define MY_INTR_CRYPTO_CLI_MUX (IRQn_Type)(3u) /* CM0+ IPC interrupt mux number the Crypto client */
#define MY_INTR_CRYPTO_ERR_MUX (IRQn_Type)(4u) /* CM0+ ERROR interrupt mux number the Crypto server */
const cy_stc_crypto_config_t myCryptoConfig =
{
/* .ipcChannel */ MY_CHAN_CRYPTO,
/* .acquireNotifierChannel */ MY_INTR_CRYPTO_SRV,
/* .releaseNotifierChannel */ MY_INTR_CRYPTO_CLI,
/* .releaseNotifierConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_CLI_MUX,
/* .cm0pSrc */ cpuss_interrupts_ipc_14_IRQn, /* depends on selected releaseNotifierChannel value */
#else
/* .intrSrc */ cpuss_interrupts_ipc_14_IRQn, /* depends on selected releaseNotifierChannel value */
#endif
/* .intrPriority */ 2u,
},
/* .userCompleteCallback */ NULL,
/* .userGetDataHandler */ NULL,
/* .userErrorHandler */ NULL,
/* .acquireNotifierConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_SRV_MUX, /* to use with DeepSleep mode should be in DeepSleep capable muxer's range */
/* .cm0pSrc */ cpuss_interrupts_ipc_13_IRQn, /* depends on selected acquireNotifierChannel value */
#else
/* .intrSrc */ cpuss_interrupts_ipc_13_IRQn, /* depends on selected acquireNotifierChannel value */
#endif
/* .intrPriority */ 2u,
},
/* .cryptoErrorIntrConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_ERR_MUX,
/* .cm0pSrc */ cpuss_interrupt_crypto_IRQn,
#else
/* .intrSrc */ cpuss_interrupt_crypto_IRQn,
#endif
/* .intrPriority */ 2u,
}
};
cy_stc_crypto_server_context_t myCryptoServerContext;
cy_en_crypto_status_t cryptoStatus;
cryptoStatus = Cy_Crypto_Server_Start_Base(&myCryptoConfig, &myCryptoServerContext);
/* ... check for errors... */

Because the two cores operate asynchronously, ensure that server initialization is complete before initializing the client. There are several ways to do this:

All crypto operations are asynchronous. To ensure that any crypto operation is complete and the result is valid, use Cy_Crypto_Sync. Use the CY_CRYPTO_SYNC_NON_BLOCKING parameter to check status. Use CY_CRYPTO_SYNC_BLOCKING to wait for the operation to complete.

Client initialization

Use Cy_Crypto_Init to initialize the Crypto client with the configuration parameters (cy_stc_crypto_config_t) and a pointer to the context (cy_stc_crypto_context_t). Do not fill in the values for the context structure.

Then call Cy_Crypto_Enable to enable the Crypto hardware IP block. After this, the Crypto driver is ready to execute crypto functions. These calls must be made on the client side. Firmware can implement the client on either core.

/* Scenario: Configure Server and Client as follows:
* Server:
* - IPC channel for communication: 13
* - IPC interrupt structure used for new request notifications: 13
* - Data request handler: default
* - Hardware errors handler: default
* Client:
* - IPC channel for communication: 13
* - IPC interrupt structure used for data ready notifications: 14
* - Data Complete callback: not used
*/
#define MY_CHAN_CRYPTO (uint32_t)(13u) /* IPC data channel for the Crypto */
#define MY_INTR_CRYPTO_SRV (uint32_t)(13u) /* IPC interrupt structure for the Crypto server */
#define MY_INTR_CRYPTO_CLI (uint32_t)(14u) /* IPC interrupt structure for the Crypto client */
#define MY_INTR_CRYPTO_SRV_MUX (IRQn_Type)(2u) /* CM0+ IPC interrupt mux number the Crypto server */
#define MY_INTR_CRYPTO_CLI_MUX (IRQn_Type)(3u) /* CM0+ IPC interrupt mux number the Crypto client */
#define MY_INTR_CRYPTO_ERR_MUX (IRQn_Type)(4u) /* CM0+ ERROR interrupt mux number the Crypto server */
const cy_stc_crypto_config_t myCryptoConfig =
{
/* .ipcChannel */ MY_CHAN_CRYPTO,
/* .acquireNotifierChannel */ MY_INTR_CRYPTO_SRV,
/* .releaseNotifierChannel */ MY_INTR_CRYPTO_CLI,
/* .releaseNotifierConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_CLI_MUX,
/* .cm0pSrc */ cpuss_interrupts_ipc_14_IRQn, /* depends on selected releaseNotifierChannel value */
#else
/* .intrSrc */ cpuss_interrupts_ipc_14_IRQn, /* depends on selected releaseNotifierChannel value */
#endif
/* .intrPriority */ 2u,
},
/* .userCompleteCallback */ NULL,
/* .userGetDataHandler */ NULL,
/* .userErrorHandler */ NULL,
/* .acquireNotifierConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_SRV_MUX, /* to use with DeepSleep mode should be in DeepSleep capable muxer's range */
/* .cm0pSrc */ cpuss_interrupts_ipc_13_IRQn, /* depends on selected acquireNotifierChannel value */
#else
/* .intrSrc */ cpuss_interrupts_ipc_13_IRQn, /* depends on selected acquireNotifierChannel value */
#endif
/* .intrPriority */ 2u,
},
/* .cryptoErrorIntrConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_ERR_MUX,
/* .cm0pSrc */ cpuss_interrupt_crypto_IRQn,
#else
/* .intrSrc */ cpuss_interrupt_crypto_IRQn,
#endif
/* .intrPriority */ 2u,
}
};
cy_stc_crypto_context_t myCryptoContext;
cy_en_crypto_status_t cryptoStatus;
cryptoStatus = Cy_Crypto_Init(&myCryptoConfig, &myCryptoContext);
/* ... check for errors... */
cryptoStatus = Cy_Crypto_Enable();
/* ... check for errors... */

Common Use Cases

CRC Calculation

To calculate CRC of a data image:

Code example:

/* CRC parameters for some CRC algorithms:
+---------------------+-----+------------+------------+------+------+-----+------------+------------+
| CRC algorithm Name | CRC | Polynom | Initial | Data | Data | Rem | Remainder | Expected |
| | len | | seed | REV | XOR | REV | XOR | CRC |
| ------------------- | --- | ---------- |----------- | ---- | ---- | --- | ---------- | ---------- |
| CRC-6 / CDMA2000-A | 6 | 0x27 | 0x3F | 0 | 0 | 0 | 0x00 | 0x0D |
| CRC-6 / CDMA2000-B | 6 | 0x07 | 0x3F | 0 | 0 | 0 | 0x00 | 0x3B |
| CRC-6 / DARC | 6 | 0x19 | 0x00 | 1 | 0 | 1 | 0x00 | 0x26 |
| CRC-6 / ITU | 6 | 0x03 | 0x00 | 1 | 0 | 1 | 0x00 | 0x06 |
| CRC-8 / ITU | 8 | 0x07 | 0x00 | 0 | 0 | 0 | 0x55 | 0xA1 |
| CRC-8 / MAXIM | 8 | 0x31 | 0x00 | 1 | 0 | 1 | 0x00 | 0xA1 |
| CRC-8 / ROHC | 8 | 0x07 | 0xFF | 1 | 0 | 1 | 0x00 | 0xD0 |
| CRC-8 / WCDMA | 8 | 0x9B | 0x00 | 1 | 0 | 1 | 0x00 | 0x25 |
| CRC-16 / CCITT-0 | 16 | 0x1021 | 0xFFFF | 0 | 0 | 0 | 0x0000 | 0x29B1 |
| CRC-16 / CDMA2000 | 16 | 0xC867 | 0xFFFF | 0 | 0 | 0 | 0x0000 | 0x4C06 |
| CRC-32 | 32 | 0x04C11DB7 | 0xFFFFFFFF | 1 | 0 | 1 | 0xFFFFFFFF | 0xCBF43926 |
| CRC-32 / BZIP2 | 32 | 0x04C11DB7 | 0xFFFFFFFF | 0 | 0 | 0 | 0xFFFFFFFF | 0xFC891918 |
+---------------------+-----+------------+------------+------+------+-----+------------+------------+
*/
/* Use "CRC-16/CCITT-0" calculation */
#define CRC_BITLEN (16u)
#define CRC_POLYNOMIAL (0x1021u)
#define CRC_LFSR_SEED (0xffffu)
#define CRC_DATA_REVERSE (0u)
#define CRC_DATA_XOR (0u)
#define CRC_REM_REVERSE (0u)
#define CRC_REM_XOR (0x0000u)
CY_ALIGN(4) uint8_t message[9] = "123456789";
CY_ALIGN(4) uint32_t calculatedCrc = 0;
cy_stc_crypto_context_crc_t cryptoCrcContext;
cy_en_crypto_status_t cryptoStatus;
/* Initialization Crypto operation */
/* Note: Polynomial and Remainder XOR values are always MSB aligned. */
cryptoStatus = Cy_Crypto_Crc_Init((CRC_POLYNOMIAL << (32 - CRC_BITLEN)),
CRC_DATA_REVERSE,
CRC_DATA_XOR,
CRC_REM_REVERSE,
(CRC_REM_XOR << (32 - CRC_BITLEN)),
&cryptoCrcContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */
/* Calculate CRC */
/* Note: Initial seed value is always MSB aligned. */
cryptoStatus = Cy_Crypto_Crc_Run((void*)message,
sizeof(message),
&calculatedCrc,
(uint32_t)(CRC_LFSR_SEED << (32u - CRC_BITLEN)),
&cryptoCrcContext);
/* ... check for errors... */
/* Wait crypto become available */
cryptoStatus = Cy_Crypto_Sync(CY_CRYPTO_SYNC_BLOCKING);
/* ... check for errors... */
/* Note: Calculated CRC value is MSB aligned and should be shifted WHEN CRC_DATA_REVERSE is zero. */
if (CRC_DATA_REVERSE == 0u)
{
calculatedCrc = calculatedCrc >> (32 - CRC_BITLEN);
}

Pseudo Random Number Generation

To generate a pseudo random number:

Code example:

/* Initialization seed values fro Pseudo Random Generator */
#define LFSR32_INITSTATE (0xd8959bc9)
#define LFSR31_INITSTATE (0x2bb911f8)
#define LFSR29_INITSTATE (0x060c31b7)
#define MAX_PRNG_VALUE (255UL)
/* Generated Random Number */
uint32_t rndNum = 0;
cy_stc_crypto_context_prng_t cryptoPrngContext;
cy_en_crypto_status_t cryptoStatus;
/* Initialize Pseudo Random Generator in Crypto Driver */
cryptoStatus = Cy_Crypto_Prng_Init(
LFSR32_INITSTATE,
LFSR31_INITSTATE,
LFSR29_INITSTATE, &cryptoPrngContext);
/* ... check for errors... */
/* Generate Pseudo Random number into rndNum variable */
cryptoStatus = Cy_Crypto_Prng_Generate(MAX_PRNG_VALUE, &rndNum, &cryptoPrngContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

True Random Number Generation

To generate a true random number:

Code example:

/* Initialization polynomial values fro True Random Generator.
Polynomial for programmable Galois ring oscillator.
The polynomial is represented WITHOUT the high order bit (this bit is always assumed '1').
GARO 31 bit polynomial (0x04c1:1db7) = x31 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
Polynomial for programmable Fibonacci ring oscillator.
The polynomial is represented WITHOUT the high order bit (this bit is always assumed '1').
FIRO 31 bit polynomial (0x04c1:1db7) = x31 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
*/
#define GARO31_INITSTATE (0x04c11db7)
#define FIRO31_INITSTATE (0x04c11db7)
#define MAX_TRNG_BIT_SIZE (32UL)
/* Generated Random Number */
uint32_t rndNum = 0;
cy_stc_crypto_context_trng_t cryptoTrngContext;
cy_en_crypto_status_t cryptoStatus;
/* Generate True Random number */
cryptoStatus = Cy_Crypto_Trng_Generate(
GARO31_INITSTATE,
FIRO31_INITSTATE,
MAX_TRNG_BIT_SIZE,
&rndNum, &cryptoTrngContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

DES encryption

To encrypt a message using the DES algorithm:

Code example:

/* Note: all keys are placed into the one keys array. */
#define STRING_LENGTH 8
CY_ALIGN(4) uint8_t msgString[STRING_LENGTH] = "Crypto!";
CY_ALIGN(4) uint8_t encString[STRING_LENGTH];
#define KEY1 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
CY_ALIGN(4) uint8_t desKey[] = { KEY1 };
cy_stc_crypto_context_des_t cryptoDesContext;
cy_en_crypto_status_t cryptoStatus;
(uint32_t *)desKey, /* Pointer to key */
(uint32_t *)encString, /* Destination string */
(uint32_t *)msgString, /* Source String */
&cryptoDesContext );
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

TDES encryption

To encrypt a message using the TDES algorithm:

Code example:

/* Note: all keys are placed into the one keys array. */
#define STRING_LENGTH 8
CY_ALIGN(4) uint8_t msgString[STRING_LENGTH] = "Crypto!";
CY_ALIGN(4) uint8_t encString[STRING_LENGTH];
#define KEY1 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
#define KEY2 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
#define KEY3 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
CY_ALIGN(4) uint8_t tdesKey[] = { KEY1, KEY2, KEY3 };
cy_stc_crypto_context_des_t cryptoDesContext;
cy_en_crypto_status_t cryptoStatus;
(uint32_t *)tdesKey, /* Pointer to keys */
(uint32_t *)encString, /* Destination string */
(uint32_t *)msgString, /* Source String */
&cryptoDesContext );
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

AES encryption

The Crypto driver provides a four AES encryption algorithms (ECB, CBC, CFB and CTR) that are used similarly.

To encrypt a message using AES ECB algorithm (as example):

Code example:

/* All data arrays should be 4-byte aligned */
CY_ALIGN(4) uint8_t aesKey[CY_CRYPTO_AES_128_KEY_SIZE] = {
0x2Bu, 0x7Eu, 0x15u, 0x16u, 0x28u, 0xAEu, 0xD2u, 0xA6u,
0xABu, 0xF7u, 0x15u, 0x88u, 0x09u, 0xCFu, 0x4Fu, 0x3Cu
};
CY_ALIGN(4) uint8_t aesEcbPlainText[CY_CRYPTO_AES_BLOCK_SIZE] = {
0x6Bu, 0xC0u, 0xBCu, 0xE1u, 0x2Au, 0x45u, 0x99u, 0x91u,
0xE1u, 0x34u, 0x74u, 0x1Au, 0x7Fu, 0x9Eu, 0x19u, 0x25u
};
CY_ALIGN(4) uint8_t aesEcbCipherText[CY_CRYPTO_AES_BLOCK_SIZE] = {0};
cy_stc_crypto_context_aes_t cryptoAesContext;
cy_en_crypto_status_t cryptoStatus;
/* Initialize Crypto AES functionality */
cryptoStatus = Cy_Crypto_Aes_Init(
(uint32_t *)aesKey,
&cryptoAesContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */
/* Encrypt one block (16Bytes) by AES128 */
cryptoStatus = Cy_Crypto_Aes_Ecb_Run(
(uint32_t*)aesEcbCipherText,
(uint32_t*)aesEcbPlainText,
&cryptoAesContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

SHA digest calculation

To calculate a SHA digest of a message:

Code example:

/* All data arrays should be 4-byte aligned */
/* Message is: "abc" */
CY_ALIGN(4) uint8_t sha256PlainText_1[3] = "abc";
/* Calculated SHA-256 digest */
CY_ALIGN(4) uint8_t sha256Digest[CY_CRYPTO_SHA256_DIGEST_SIZE] = {0};
cy_stc_crypto_context_sha_t cryptoShaContext;
cy_en_crypto_status_t cryptoStatus;
/* Hash the message by SHA256 */
cryptoStatus = Cy_Crypto_Sha_Run(
(uint32_t *)sha256PlainText_1,
sizeof(sha256PlainText_1),
(uint32_t *)sha256Digest,
&cryptoShaContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

CMAC calculation

To calculate CMAC of a message:

Code example:

/* All data arrays should be 4-byte aligned */
CY_ALIGN(4) uint8_t cmacKey[CY_CRYPTO_AES_256_KEY_SIZE] =
{
0x60u, 0x3Du, 0xEBu, 0x10u, 0x15u, 0xCAu, 0x71u, 0xBEu,
0x2Bu, 0x73u, 0xAEu, 0xF0u, 0x85u, 0x7Du, 0x77u, 0x81u,
0x1Fu, 0x35u, 0x2Cu, 0x07u, 0x3Bu, 0x61u, 0x08u, 0xD7u,
0x2Du, 0x98u, 0x10u, 0xA3u, 0x09u, 0x14u, 0xDFu, 0xF4u
};
CY_ALIGN(4) uint8_t cmac256PlainText[16] =
{
0x6Bu, 0xC1u, 0xBEu, 0xE2u, 0x2Eu, 0x40u, 0x9Fu, 0x96u,
0xE9u, 0x3Du, 0x7Eu, 0x11u, 0x73u, 0x93u, 0x17u, 0x2Au
};
/* Calculated CMAC */
CY_ALIGN(4) uint8_t cmac[CY_CRYPTO_AES_BLOCK_SIZE] = {0};
cy_stc_crypto_context_aes_t cryptoAesContext;
cy_en_crypto_status_t cryptoStatus;
/* CMAC from the text message using AES-256 */
cryptoStatus = Cy_Crypto_Aes_Cmac_Run(
(uint32_t *)cmac256PlainText,
sizeof(cmac256PlainText),
(uint32_t *)cmacKey,
(uint32_t *)cmac,
&cryptoAesContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

HMAC calculation

To calculate HMAC of a message:

Code example:

/* All data arrays should be 4-byte aligned */
CY_ALIGN(4) uint8_t hmac256Key[32] =
{
0x00u, 0x01u, 0x02u, 0x03u, 0x04u, 0x05u, 0x06u, 0x07u,
0x08u, 0x09u, 0x0Au, 0x0Bu, 0x0Cu, 0x0Du, 0x0Eu, 0x0Fu,
0x10u, 0x11u, 0x12u, 0x13u, 0x14u, 0x15u, 0x16u, 0x17u,
0x18u, 0x19u, 0x1Au, 0x1Bu, 0x1Cu, 0x1Du, 0x1Eu, 0x1Fu
};
CY_ALIGN(4) uint8_t hmac256PlainText[34] =
{
0x53u, 0x61u,
0x6Du, 0x70u, 0x6Cu, 0x65u, 0x20u, 0x6Du, 0x65u, 0x73u,
0x73u, 0x61u, 0x67u, 0x65u, 0x20u, 0x66u, 0x6Fu, 0x72u,
0x20u, 0x6Bu, 0x65u, 0x79u, 0x6Cu, 0x65u, 0x6Eu, 0x3Cu,
0x62u, 0x6Cu, 0x6Fu, 0x63u, 0x6Bu, 0x6Cu, 0x65u, 0x6Eu
};
/* Calculated HMAC */
CY_ALIGN(4) uint8_t hmac256[CY_CRYPTO_SHA256_DIGEST_SIZE] = {0};
cy_stc_crypto_context_sha_t cryptoShaContext;
cy_en_crypto_status_t cryptoStatus;
/* HMAC from the text message using SHA256 */
cryptoStatus = Cy_Crypto_Hmac_Run(
(uint32_t *)hmac256,
(uint32_t *)hmac256PlainText,
sizeof(hmac256PlainText),
(uint32_t *)hmac256Key,
sizeof(hmac256Key),
&cryptoShaContext);
/* ... check for errors... */
/* Wait crypto become available */
/* ... check for errors... */

RSA signature verification

To verify the RSA signature of the data image:

Code example:

#define RSA_MODULO_LENGTH 2048u
#define RSA_MODULO_DATA_SIZE (RSA_MODULO_LENGTH / 8)
typedef struct
{
cy_stc_crypto_rsa_pub_key_t publicKeyStruct;
uint8_t moduloData[RSA_MODULO_DATA_SIZE];
uint8_t expData [32];
uint8_t k1Data [RSA_MODULO_DATA_SIZE+4];
uint8_t k2Data [RSA_MODULO_DATA_SIZE];
uint8_t k3Data [RSA_MODULO_DATA_SIZE];
} cy_stc_public_key_t;
/* All data arrays should be 4-byte aligned */
cy_stc_public_key_t cy_publicKey =
{
.publicKeyStruct =
{
.moduloPtr = 0,
.moduloLength = RSA_MODULO_LENGTH,
.pubExpPtr = 0,
.pubExpLength = 24,
.barretCoefPtr = NULL,
.inverseModuloPtr = NULL,
.rBarPtr = NULL
},
.moduloData =
{ /* modulus in Little Endian for a public key - rsa_public.txt */
0xD9u, 0x94u, 0x94u, 0x38u, 0xA4u, 0xE0u, 0x50u, 0xA1u,
0xADu, 0xC5u, 0xE2u, 0x66u, 0xA9u, 0x7Fu, 0xE7u, 0xD7u,
0xA8u, 0x10u, 0x87u, 0x3Au, 0xBEu, 0xB3u, 0x0Eu, 0x6Au,
0xB2u, 0x8Bu, 0x2Eu, 0x8Du, 0xC2u, 0x45u, 0x41u, 0xA6u,
0xDBu, 0xEBu, 0x90u, 0x20u, 0x56u, 0xECu, 0xFDu, 0x8Cu,
0x23u, 0x09u, 0x13u, 0x5Du, 0x65u, 0xA2u, 0xADu, 0x9Du,
0x3Bu, 0xF2u, 0x5Bu, 0xF6u, 0xABu, 0x2Eu, 0xFEu, 0xF2u,
0x69u, 0x98u, 0x29u, 0x6Du, 0xD1u, 0x2Eu, 0x91u, 0x5Au,
0x65u, 0x83u, 0xF5u, 0x7Fu, 0x8Eu, 0x73u, 0xFFu, 0xA1u,
0x8Cu, 0x70u, 0x07u, 0xDFu, 0x4Du, 0xF4u, 0x79u, 0xB7u,
0x18u, 0xC1u, 0xA3u, 0x2Bu, 0x82u, 0x5Bu, 0x9Eu, 0xE2u,
0xF0u, 0xA0u, 0xB8u, 0xDAu, 0x19u, 0xADu, 0xBBu, 0x2Bu,
0xD5u, 0x07u, 0x5Au, 0x85u, 0x12u, 0x03u, 0xD8u, 0x60u,
0x53u, 0x3Du, 0xC6u, 0x34u, 0xE2u, 0x7Fu, 0x96u, 0x4Cu,
0x26u, 0x1Eu, 0x82u, 0xB1u, 0x85u, 0xC3u, 0x0Du, 0x54u,
0x68u, 0x37u, 0x97u, 0x58u, 0x19u, 0x36u, 0x43u, 0x9Cu,
0xD9u, 0xC3u, 0x42u, 0xEBu, 0xBEu, 0xE2u, 0x8Fu, 0x72u,
0xF1u, 0x5Eu, 0x2Au, 0x15u, 0x56u, 0x52u, 0xD4u, 0x6Du,
0x61u, 0x97u, 0x16u, 0xFEu, 0xC3u, 0xF9u, 0x17u, 0x3Cu,
0x37u, 0xD5u, 0xE1u, 0xA1u, 0x0Au, 0xB7u, 0xD9u, 0x65u,
0xA1u, 0x15u, 0xECu, 0xC7u, 0x39u, 0xECu, 0xEDu, 0x39u,
0x98u, 0x96u, 0x66u, 0x50u, 0x8Cu, 0x25u, 0xC3u, 0x29u,
0xB9u, 0xF8u, 0x25u, 0x55u, 0x92u, 0x7Au, 0xBFu, 0xFBu,
0x45u, 0x2Au, 0x28u, 0x8Au, 0xF9u, 0xE5u, 0xE2u, 0x30u,
0x72u, 0x0Eu, 0x0Au, 0x1Cu, 0x25u, 0x09u, 0x86u, 0x6Fu,
0xF6u, 0x6Fu, 0x15u, 0xEDu, 0x14u, 0xE6u, 0x1Eu, 0x53u,
0x5Au, 0x15u, 0x25u, 0xB9u, 0x5Eu, 0xC9u, 0xBAu, 0x48u,
0xA3u, 0xE3u, 0x93u, 0x62u, 0x3Cu, 0x6Cu, 0x3Cu, 0x83u,
0x17u, 0x29u, 0xFBu, 0xAEu, 0x91u, 0x47u, 0xC9u, 0x41u,
0x2Fu, 0xF9u, 0x82u, 0x29u, 0x7Bu, 0xB4u, 0x5Au, 0x93u,
0x01u, 0x95u, 0xDBu, 0x08u, 0x7Eu, 0x7Bu, 0x99u, 0x1Eu,
0xD4u, 0x25u, 0xD3u, 0x2Au, 0xF4u, 0xC7u, 0x9Fu, 0xB3u,
},
/* Little endian exponent for a public key - rsa_public.txt */
.expData = { 0x01, 0x00, 0x01 },
.k1Data = { 0, },
.k2Data = { 0, },
.k3Data = { 0, },
};
/* Present encrypted signature of the image */
CY_ALIGN(4) uint8_t rsaEncryptedSign[RSA_MODULO_DATA_SIZE] =
{
0x12u, 0xCEu, 0x13u, 0x83u, 0x4Fu, 0xFFu, 0x39u, 0x9Bu,
0x33u, 0xDEu, 0xDCu, 0xDBu, 0x7Cu, 0x62u, 0xA5u, 0x10u,
0x24u, 0x07u, 0xEAu, 0x4Cu, 0x04u, 0x4Au, 0xCEu, 0x7Bu,
0x8Cu, 0xD8u, 0xD4u, 0x64u, 0xD1u, 0x98u, 0xA2u, 0x33u,
0x2Eu, 0xFFu, 0x06u, 0x13u, 0xD6u, 0x5Au, 0x50u, 0x3Cu,
0xA2u, 0x5Eu, 0xE3u, 0x11u, 0x54u, 0x7Cu, 0x6Au, 0x49u,
0x39u, 0xA3u, 0x62u, 0x02u, 0x66u, 0xB0u, 0x19u, 0x82u,
0xFBu, 0x5Du, 0x15u, 0xB2u, 0x0Bu, 0xF7u, 0xECu, 0x6Cu,
0xBEu, 0xEBu, 0x04u, 0x1Fu, 0x0Bu, 0x5Bu, 0x18u, 0x0Eu,
0x96u, 0x03u, 0xC4u, 0x1Eu, 0x56u, 0xB6u, 0x1Fu, 0xF2u,
0x08u, 0x7Au, 0x81u, 0x96u, 0x86u, 0xA4u, 0x93u, 0x5Du,
0x66u, 0x63u, 0x10u, 0xD5u, 0x9Bu, 0xA9u, 0xD7u, 0x52u,
0xCDu, 0xEFu, 0x23u, 0xDCu, 0x58u, 0xC7u, 0x3Du, 0x72u,
0x3Fu, 0x09u, 0x5Eu, 0x3Bu, 0x03u, 0xF9u, 0x91u, 0x10u,
0x63u, 0x2Au, 0x56u, 0xFAu, 0xCEu, 0x8Cu, 0x8Au, 0xBFu,
0xB5u, 0xA1u, 0xA7u, 0x0Fu, 0xBBu, 0xD8u, 0xACu, 0x7Fu,
0x43u, 0x3Cu, 0xBFu, 0x11u, 0xD8u, 0xAEu, 0x55u, 0xE7u,
0x23u, 0xF8u, 0xA4u, 0xE0u, 0x56u, 0xCFu, 0x3Fu, 0x21u,
0xA9u, 0xB6u, 0x01u, 0x34u, 0xDFu, 0xB6u, 0xDCu, 0xAAu,
0x29u, 0xB0u, 0x97u, 0x9Du, 0xD9u, 0xAAu, 0x47u, 0xD4u,
0xD9u, 0x4Au, 0x97u, 0x52u, 0x3Cu, 0xB9u, 0x36u, 0xDAu,
0xF6u, 0xA6u, 0x14u, 0x97u, 0xDEu, 0xE6u, 0x76u, 0xBFu,
0x1Bu, 0x23u, 0xDBu, 0x68u, 0x66u, 0xEAu, 0x0Eu, 0xC6u,
0xD3u, 0x52u, 0x05u, 0x86u, 0x26u, 0x04u, 0x31u, 0xCEu,
0xCFu, 0x8Du, 0x13u, 0x84u, 0x81u, 0xF2u, 0x7Bu, 0xB7u,
0xDCu, 0x93u, 0x23u, 0x93u, 0xD1u, 0x0Cu, 0xF4u, 0xDFu,
0x37u, 0x44u, 0x3Du, 0xD7u, 0xAFu, 0xBFu, 0xAFu, 0x32u,
0xE7u, 0x31u, 0x50u, 0x70u, 0x62u, 0xC3u, 0xABu, 0x31u,
0x51u, 0x28u, 0x2Bu, 0x0Bu, 0x31u, 0xC6u, 0xD3u, 0x0Fu,
0x74u, 0xE6u, 0x31u, 0x21u, 0xF0u, 0xA3u, 0x40u, 0x5Du,
0xEDu, 0xFFu, 0xC9u, 0xEBu, 0x5Au, 0x65u, 0xF3u, 0xCBu,
0x67u, 0x50u, 0x62u, 0x2Du, 0x8Au, 0xEAu, 0xC3u, 0xBDu,
};
/* Previously calculated SHA-256 digest of the image */
CY_ALIGN(4) uint8_t sha256Digest[CY_CRYPTO_SHA256_DIGEST_SIZE] =
{
0xf4u, 0xcbu, 0x29u, 0x53u, 0xadu, 0xc2u, 0xbdu, 0x49u,
0x6bu, 0x43u, 0x28u, 0x2bu, 0x63u, 0xb7u, 0x0du, 0x16u,
0xa9u, 0xd4u, 0x90u, 0x65u, 0x6eu, 0x7bu, 0x6du, 0xb7u,
0x0bu, 0x17u, 0xefu, 0x60u, 0x02u, 0x12u, 0x0fu, 0x90u
};
/* Temporary decryption buffer */
CY_ALIGN(4) uint8_t rsaOutput[RSA_MODULO_DATA_SIZE];
cy_stc_crypto_context_rsa_t cryptoRsaContext;
cy_stc_crypto_context_rsa_ver_t cryptoRsaVerContext;
cy_en_crypto_status_t cryptoStatus;
cy_publicKey.publicKeyStruct.moduloPtr = cy_publicKey.moduloData;
cy_publicKey.publicKeyStruct.pubExpPtr = cy_publicKey.expData;
/* NOTE: When signature placed as BIG-endian data (ex. generated by openssl), we should invert it for processing */
Cy_Crypto_InvertEndianness(rsaEncryptedSign, 256);
cryptoStatus = Cy_Crypto_Rsa_Proc(
&cy_publicKey.publicKeyStruct,
(const uint32_t*)rsaEncryptedSign, sizeof(rsaEncryptedSign),
(uint32_t *)rsaOutput,
&cryptoRsaContext);
/* ... check for errors... */
/* ... check for errors... */
Cy_Crypto_InvertEndianness(rsaOutput, 256);
/* Verify decrypted signature by calculated SHA digest from data image */
cryptoStatus = Cy_Crypto_Rsa_Verify(
&verResult,
(const uint32_t*)sha256Digest,
(const uint32_t*)rsaOutput, sizeof(rsaOutput),
&cryptoRsaVerContext);
/* ... check for errors... */
/* Wait crypto become available */
cryptoStatus = Cy_Crypto_Sync(CY_CRYPTO_SYNC_BLOCKING);
/* ... check for errors... */
if (CY_CRYPTO_RSA_VERIFY_SUCCESS == verResult)
{
/* Verification OK */
}
else
{
/* Verification FAILED */
}

RSA Usage Considerations

General RSA encryption and decryption is supported. Cy_Crypto_Rsa_Proc encrypts or decrypts data based on the parameters passed to the function. If you pass in plain text and a public key, the output is encrypted (cipher text). If you pass in cipher text and a private key, the output is decrypted (plain text).

One parameter for this function call is a structure that defines the key: cy_stc_crypto_rsa_pub_key_t. The four modulus and exponent fields are mandatory. They represent the data for either the public or private key as appropriate.

Note
The modulus and exponent values in the cy_stc_crypto_rsa_pub_key_t must be in little-endian order.
Use the Cy_Crypto_InvertEndianness function to convert to or from little-endian order.

The remaining fields represent three pre-calculated coefficients that can reduce execution time by up to 5x. The fields are: coefficient for Barrett reduction, binary inverse of the modulus, and the result of (2^moduloLength mod modulo). These fields are optional, and can be set to NULL.

Calculate these coefficients with Cy_Crypto_Rsa_CalcCoefs. Pass them in the address of the key structure with the modulus and exponent values for the key. The function returns the coefficients for the key in the key structure, replacing any previous values.

The RSA functionality also implements functions to decrypt a signature using a public key. This signature must follow the RSASSA-PKCS-v1_5 standard. The signature must contain a SHA digest (hash). MD2, MD4, and MD5 message digests are not supported.

An encrypted signature is stored as big-endian data. It must be inverted for RSA processing. To use the provided signature decryption, firmware must

  1. Calculate the SHA digest of the data to be verified with Cy_Crypto_Sha_Run.
  2. Ensure that the RSA signature is in little-endian format. Use Cy_Crypto_InvertEndianness.
  3. Decrypt the RSA signature with a public key, by calling Cy_Crypto_Rsa_Proc.
  4. Invert the byte order of the output, to return to big-endian format. Use Cy_Crypto_InvertEndianness.
  5. Call Cy_Crypto_Rsa_Verify (which requires data in big-endian format).

Implementing Crypto Interrupts

The Crypto driver uses three interrupts:

You can modify default behavior for each interrupt.

Notify Interrupt: the Crypto server has a default ISR to handle this interrupt, Cy_Crypto_Server_GetDataHandler. The default ISR clears the interrupt, retrieves the data from the IPC channel, and dispatches control to the desired cryptographic operation.

To use the default handler, set the userGetDataHandler field of the cy_stc_crypto_config_t structure to NULL.

To override, populate this field with your ISR. Then call Crypto Server Start function. Your ISR can perform additional tasks required by your application logic, but must also call Cy_Crypto_Server_GetDataHandler to dispatch the data to the correct cryptographic operation.

Note
Cy_Crypto_Server_Process should be performed from this ISR or from any other task in the multi-task environment.
void My_Crypto_Server_GetDataHandler()
{
uint32_t interruptMasked;
/*
* Check that there is really the IPC Crypto Notify interrupt,
* because the same line can be used for the IPC Crypto Release interrupt.
*/
interruptMasked = Cy_IPC_Drv_ExtractAcquireMask(Cy_IPC_Drv_GetInterruptStatusMasked(
Cy_IPC_Drv_GetIntrBaseAddr(cy_crypto_serverContext->acquireNotifierChannel)));
if ((1uL << (cy_crypto_serverContext->ipcChannel)) == interruptMasked )
{
/* Load IPC message and save it to process*/
/* Process given message */
Cy_IPC_Drv_ClearInterrupt(Cy_IPC_Drv_GetIntrBaseAddr(cy_crypto_serverContext->acquireNotifierChannel),
CY_IPC_NO_NOTIFICATION, interruptMasked);
}
}
/* Scenario: Configure Server and Client as follows:
* Server:
* - IPC channel for communication: 13
* - IPC interrupt structure used for new request notifications: 13
* - Data request handler: myGetDataHandler
* - Hardware errors handler: default
* Client:
* - IPC channel for communication: 13
* - IPC interrupt structure used for data ready notifications: 14
* - Data Complete callback: not used
*/
#define MY_CHAN_CRYPTO (uint32_t)(13u) /* IPC data channel for the Crypto */
#define MY_INTR_CRYPTO_SRV (uint32_t)(13u) /* IPC interrupt structure for the Crypto server */
#define MY_INTR_CRYPTO_CLI (uint32_t)(14u) /* IPC interrupt structure for the Crypto client */
#define MY_INTR_CRYPTO_SRV_MUX (IRQn_Type)(2u) /* CM0+ IPC interrupt mux number the Crypto server */
#define MY_INTR_CRYPTO_CLI_MUX (IRQn_Type)(3u) /* CM0+ IPC interrupt mux number the Crypto client */
#define MY_INTR_CRYPTO_ERR_MUX (IRQn_Type)(4u) /* CM0+ ERROR interrupt mux number the Crypto server */
const cy_stc_crypto_config_t myCryptoConfig =
{
/* .ipcChannel */ MY_CHAN_CRYPTO,
/* .acquireNotifierChannel */ MY_INTR_CRYPTO_SRV,
/* .releaseNotifierChannel */ MY_INTR_CRYPTO_CLI,
/* .releaseNotifierConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_CLI_MUX,
/* .cm0pSrc */ cpuss_interrupts_ipc_14_IRQn, /* depends on selected releaseNotifierChannel value */
#else
/* .intrSrc */ cpuss_interrupts_ipc_14_IRQn, /* depends on selected releaseNotifierChannel value */
#endif
/* .intrPriority */ 2u,
},
/* .userCompleteCallback */ NULL,
/* .userGetDataHandler */ My_Crypto_Server_GetDataHandler,
/* .userErrorHandler */ NULL,
/* .acquireNotifierConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_SRV_MUX, /* to use with DeepSleep mode should be in DeepSleep capable muxer's range */
/* .cm0pSrc */ cpuss_interrupts_ipc_13_IRQn, /* depends on selected acquireNotifierChannel value */
#else
/* .intrSrc */ cpuss_interrupts_ipc_13_IRQn, /* depends on selected acquireNotifierChannel value */
#endif
/* .intrPriority */ 2u,
},
/* .cryptoErrorIntrConfig */ {
#if (CY_CPU_CORTEX_M0P)
/* .intrSrc */ MY_INTR_CRYPTO_ERR_MUX,
/* .cm0pSrc */ cpuss_interrupt_crypto_IRQn,
#else
/* .intrSrc */ cpuss_interrupt_crypto_IRQn,
#endif
/* .intrPriority */ 2u,
}
};
cy_stc_crypto_server_context_t myCryptoServerContext;
cy_en_crypto_status_t cryptoStatus;
cryptoStatus = Cy_Crypto_Server_Start_Base(&myCryptoConfig, &myCryptoServerContext);
/* ... check for errors... */

Release Interrupt: The Crypto driver includes a handler for this interrupt. The interrupt handler clears the interrupt and calls a user-provided callback routine. You cannot override this interrupt handler. By default the interrupt is disabled.

To use default behavior (interrupt disabled), set the userCompleteCallback field of the cy_stc_crypto_config_t structure to NULL. To enable the interrupt, populate this field with your callback function. Then call Cy_Crypto_Init. If the callback function is not NULL, the Init function enables the interrupt, and default behavior calls your routine.

When performing cryptographic operations, firmware must ensure the operation is complete before acting on the results. If the release interrupt is disabled, typically calls to Cy_Crypto_Sync should be blocking. If the interrupt is enabled, your callback function is called when the operation is complete. This lets you avoid blocking calls to Cy_Crypto_Sync.

Error Interrupt: The Crypto server has a default ISR to handle this interrupt. It clears the interrupt and sets an internal flag that an error has occurred.

To use the default handler, set the userErrorHandler field of the cy_stc_crypto_config_t structure to NULL. To override, populate this field with your ISR. Then call Crypto Server Start function.

Your ISR must call Cy_Crypto_Server_ErrorHandler, and can perform any additional tasks required by your application logic.

API Reference

 Macros
 
 Functions
 
 Data Structures