Intel® QAT Cryptographic API

This section describes the sample code for the Intel® QuickAssist Technology Cryptographic API, beginning with an API overview, and followed by descriptions of various scenarios to illustrate the usage of the API.

Overview

The Intel® QuickAssist Technology Cryptographic API can be categorized into the following broad areas:

  • Common: This is defined by the file cpa_cy_common.h. This includes the functionality for the initialization and shutdown of the service.

  • Instance Management: The file cpa_cy_im.h defines the functions for managing instances. A given implementation of the API can present multiple instances of the cryptographic service, each representing a logical or virtual device. Request order is guaranteed within a given instance of the service.

  • Symmetric: The following files constitute the symmetric API:

    • The cpa_cy_sym.h file contains the symmetric API, used for ciphers, hashing/message digests, algorithm chaining (combining cipher and hash into a single call), and authenticated ciphers.

    • The cpa_sy_sym_dp.h file also contains the symmetric API, used for ciphers, hashing/message digest, algorithm chaining (combining cipher and hash into a single call) and authenticated ciphers. This API is recommended for data plane applications, in which the cost of offload (i.e. the cycles consumed by the API in sending requests to the hardware and processing the responses) needs to be minimized. Several constraints need to be acceptable to the application. To use this API, these are listed in the Data Plane APIs section.

    • The cpa_cy_key.h file contains the API for key generation for Secure Sockets Layer (SSL) and Transport Layer Security (TLS).

  • Asymmetric: The following files constitute the asymmetric API:

    • The cpa_cy_rsa.h file defines the API for RSA.

    • The cpa_cy_dsa.h file defines the API for Digital Signature Algorithm (DSA).

    • The cpa_cy_dh.h file defines the API for Diffie-Hellman.

    • The cpa_cy_ec.h file defines the API for base elliptic curve cryptography.

    • The cpa_cy_ecdsa.h file defines the API for Elliptic Diffie (EC) DSA.

    • The cpa_cy_ecdh.h file defines the API for Elliptic Diffie-Hellman (ECDH).

    • The cpa_cy_prime.h file defines the API for prime number testing.

    • The cpa_cy_ln.h file defines the API for a large number of math operations, such as modular exponentiation, etc.

    • The cpa_cy_ecsm2.h file defines the API for SM2 algorithm which is based on Elliptic Curves Cryptography (ECC)

  • Random Bit Generation (RBG): The following files constitute the RBG API and have been deprecated because random bit generation can be handled in the CPU:

    • The cpa_cy_drbg.h file defines the API for deterministic random bit generation.

    • The cpa_cy_nrbg.h file defines the API for a non-deterministic random bit generation.

The cryptographic API uses the base API, which defines base data types used across all services of the Intel® QAT API.

Sessions

The symmetric API is the only API with the concept of sessions. The meaning of a session within the symmetric API is defined below.

Priority

The cryptographic symmetric API has support for priorities. Priority can be specified on a per-session basis. Two levels of priority are supported: high priority and normal priority. Implementations may use a strict priority order or a weighted round robinbased priority scheme.

Using the Symmetric Cryptography API

This section contains examples of how to use the symmetric API. It describes general concepts and how to use the symmetric API to perform various types of cipher and hash.

Note

Examples are simplified and demonstrate how to use the APIs and build the structures required for various use cases.

These examples may not demonstrate the optimal way to use the API to get maximum performance for a particular implementation. Refer to Related Documents and References for implementation specific documentation and performance sample code for a guide on how to use the API for best performance.

All of the symmetric examples follow the same basic steps:

  • Define a callback function (if the API is to be invoked asynchronously).

  • Discover and start up the cryptographic service instance.

  • Create and initialize a session.

  • Invoke multiple symmetric operations (cipher and/or hash) on the session.

  • Tear down the session.

  • Stop the cryptographic service instance.

General Concepts

This section describes the following concepts:

  • Session

  • Place and Out-of-Place Support

  • Partial Support

Session

In case of the symmetric API, a session is a handle that describes the cryptographic parameters to be applied to several buffers. This might be the buffers within a single file or all the packets associated with a particular Internet Protocol Security (IPSec) tunnel or security association. The data within a session handle includes the following:

  • The operation (cipher, hash, or both, and if both, the order in which the algorithms should be applied).

  • The cipher setup data, including the cipher algorithm and mode, the key and its length, and the direction (encrypt or decrypt).

  • The hash setup data, including the hash algorithm, mode (plain, nested or authenticated), and digest result length (to allow for truncation).

    • The authenticated mode can refer to Hashed Message Authenticate Code (HMAC), which requires that the key and its length are also specified. It is also used for Galois Counter Mode (GCM), and Counter mode with Cipher-block Chaining Message authentication code (CCM) authenticated encryption, in which case the Additional Authenticated Data (AAD) length is also specified.

    • For nested mode, the inner and outer prefix data and length are specified, as well as the outer hash algorithm.

In-Place and Out-of-Place Support

An In-Place operation means that the destination buffer is the same as the source buffer. An Out-of-Place operation means that the destination buffer is different from the source buffer.

Partial Support

Most of the examples in this section operate on full packets, as indicated by the packetType of CPA_CY_SYM_PACKET_TYPE_FULL. The API also supports operating in partial mode, where, for example, state (e.g., cipher state) needs to be carried forward from one packet/record to the next. In the Hash a File section, there is an example of hashing a file that uses the partial API.

Note

  • The size of the data to be hashed or ciphered must be a multiple of the block size of the algorithm for all partial packets.

  • For hash/authentication, the digest verify flag only applies to the last partial packet.

  • For algorithm chaining, only the cipher state is maintained between calls. The hash state is not maintained between calls; instead, the hash digest is generated/verified for each call. The size of the data to be ciphered must be a multiple of the block size of the algorithm for all partial packets. The size of the data to be hashed does not have this restriction. If both the cipher state and the hash state need to be maintained between calls, then algorithm chaining cannot be used.

Cipher

This example demonstrates the usage of the symmetric API, specifically using this API to perform a cipher operation. It encrypts some sample text using the AES-256 algorithm in Cipher Block Chaining (CBC) mode.

These samples are located in quickassist/lookaside/access_layer/src/sample_code/functional/sym/cipher_sample.

The following subsections describe the main functions in this file.

symCallback

A callback function must be supplied to use the API in asynchronous mode, and this function is called back (that is, invoked by the implementation of the API) when the asynchronous operation has completed. The context in which it is invoked depends on the implementation. For example, it could be invoked in the context of a Linux* interrupt handler’s bottom half or in the context of a user created polling thread. The context in which this function is invoked places restrictions on what processing can be done in the callback function. On the API, it states that this function should not sleep (since it may be called in a context that does not permit sleeping, for example, a Linux* bottom half).

This function can perform whatever processing is appropriate for the application. For example, it may free memory, continue the processing of a decrypted packet, etc. In the below example, the function only sets the complete variable to indicate it has been called.

static void symCallback(void *pCallbackTag,
                        CpaStatus status,
                        const CpaCySymOp operationType,
                        void *pOpData,
                        CpaBufferList *pDstBuffer,
                        CpaBoolean verifyResult)
{
    PRINT_DBG("Callback called with status = %d.\n", status);

    if (NULL != pCallbackTag) {
        /* Indicate that the function has been called */
        COMPLETE((struct COMPLETION_STRUCT *)pCallbackTag);
    }
}

cipherSample

This is the main entry point for the sample cipher code. It demonstrates the sequence of calls to be made to the API to create a session, perform one or more cipher operations, and then tear down the session.

First, call the instance discovery utility function - sampleCyGetInstance - which is a simplified version of instance discovery, in which exactly one instance of a crypto service is discovered. It does this by querying the API for all instances, and returning the first instance, as illustrated in the below example.

Note

This step is described in the Instance Discovery section, but is repeated here for convenience.

#ifdef DO_CRYPTO
void sampleCyGetInstance(CpaInstanceHandle* pCyInstHandle)
{
    CpaInstanceHandle cyInstHandles[MAX_INSTANCES];
    Cpa16U numInstances = 0;
    CpaStatus status = CPA_STATUS_SUCCESS;

    *pCyInstHandle = NULL;

    status = cpaCyGetNumInstances(&numInstances);
    if ((status == CPA_STATUS_SUCCESS) && (numInstances > 0))
    {
        status = cpaCyGetInstances(MAX_INSTANCES, cyInstHandles);
        if (status == CPA_STATUS_SUCCESS)
        {
            *pCyInstHandle = cyInstHandles[0];
        }
    }
    if (0 == numInstances)
    {
        PRINT_ERR("No instances found for 'SSL'\n");
        PRINT_ERR("Please check your section names in the config file.\n");
        PRINT_ERR("Also make sure to use config file version 2.\n");
    }
}
#endif

The below example sets the address translation function for the instance. This function will be used by the API to convert virtual addresses to physical addresses.

status = cpaCySetAddressTranslation(cyInstHandle, sampleVirtToPhys);

Start the crypto service running as shown below.

status = cpaCyStartInstance(cyInstHandle);

The next step is to create and initialize a session. First, populate the fields of the session initialization operational data structure.

Note

The size required to store a session is implementation-dependent, so you must query the API first to determine how much memory to allocate, and then allocate that memory.

One of two available queries can be used:

  • cpaCySymSessionCtxGetSize(const CpaInstanceHandle instanceHandle_in, const CpaCySymSessionSetupData *pSessionSetupData, Cpa32U *pSessionCtxSizeInBytes)

    • This will always return the maximum session context size (i.e., the full size of the session including padding and other session state information). See example below.

  • cpaCySymSessionCtxGetDynamicSize(const CpaInstanceHandle instanceHandle_in, const CpaCySymSessionSetupData *pSessionSetupData, Cpa32U *pSessionCtxSizeInBytes)

    • This query can be used instead to return a reduced memory size, based on whether the use case meets certain session setup criteria. See example below.

    • This query will return one of three values for pSessionCtxSizeInBytes as follows:

      • If partial packets are not being used and the symmetric operation is AuthEncrypt (i.e., the cipher and hash algorithms are either CCM or GCM), the size returned will be approximately half of the standard size.

      • If partial packets are not being used and the cipher algorithm is not ARC4, Snow3g_UEA2, AES_CCM or AES_GCM, and the hash algorithm is not Snow3G_UIA2, AES_CCM or AES_GCM, and Hash Mode is not Auth, the size returned will be between half and one third of the standard size.

      • In all other cases, the standard size is returned.

The following parameter exists in the CpaCySymSessionSetupData structure: CpaBoolean partialsNotRequired

This flag indicates if partial packet processing is required for the session. If partial packets are not being used and the preference is to use one of the reduced session memory sizes, set this flag to CPA_TRUE before calling the cpaCySymSessionCtxGetDynamicSize() function.

Note

The equivalent reduced memory context query for Data Plane API is: cpaCySymDpSessionCtxGetDynamicSize(const CpaInstanceHandle instanceHandle_in, const CpaCySymSessionSetupData *pSessionSetupData, Cpa32U *pSessionCtxSizeInBytes). Refer to section Chained Cipher and Hash Using the Symmetric Data Plane API) for more details.

/* Populate the session setup structure for the operation required */
sessionSetupData.sessionPriority = CPA_CY_PRIORITY_NORMAL;
sessionSetupData.symOperation = CPA_CY_SYM_OP_CIPHER;
sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

/* Determine size of session context to allocate */
PRINT_DBG("cpaCySymSessionCtxGetSize\n");
status = cpaCySymSessionCtxGetSize(cyInstHandle, &sessionSetupData, &sessionCtxSize);

if (CPA_STATUS_SUCCESS == status) {
    /* Allocate session context */
    status = PHYS_CONTIG_ALLOC(&sessionCtx, sessionCtxSize);
}

/* Initialize the Cipher session */
if (CPA_STATUS_SUCCESS == status) {
    PRINT_DBG("cpaCySymInitSession\n");
    status = cpaCySymInitSession(
        cyInstHandle,
        symCallback, /* Callback function */
        &sessionSetupData, /* Session setup data */
        sessionCtx); /* Output of the function */
}

The next step is to call the function cipherPerformOp, which actually performs the cipher operation. This in turn performs the following steps:

  • Memory Allocation: Different implementations of the API require different amounts of space to store metadata associated with buffer lists. Query the API to find out how much space the current implementation needs, and then allocate space for the buffer metadata, the buffer list, and for the buffer itself. You must also allocate memory for the initialization vector. See below example. The memory for the source buffer and initialization vector is populated with the required data.

    status = cpaCyBufferListGetMetaSize(cyInstHandle, numBuffers, &bufferMetaSize);
    if (CPA_STATUS_SUCCESS == status) {
        status = PHYS_CONTIG_ALLOC(&pBufferMeta, bufferMetaSize);
    }
    if (CPA_STATUS_SUCCESS == status) {
        status = OS_MALLOC(&pBufferList, bufferListMemSize);
    }
    if (CPA_STATUS_SUCCESS == status) {
        status = PHYS_CONTIG_ALLOC(&pSrcBuffer, bufferSize);
    }
    if (CPA_STATUS_SUCCESS == status) {
        status = PHYS_CONTIG_ALLOC(&pIvBuffer, sizeof(sampleCipherIv));
    }
    
  • Set Up Operational Data: Populate the structure containing the operational data that is needed to run the algorithm as shown below.

    pOpData->sessionCtx = sessionCtx;
    pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
    pOpData->pIv = pIvBuffer;
    pOpData->ivLenInBytes = sizeof(sampleCipherIv);
    pOpData->cryptoStartSrcOffsetInBytes = 0;
    pOpData->messageLenToCipherInBytes = sizeof(sampleCipherSrc);
    
  • Perform Operation: Initialize the completion variable, which is used by the callback function to indicate that the operation is complete, then perform the operation. See below example.

    COMPLETION_INIT(&complete);
    
    status = cpaCySymPerformOp(
        cyInstHandle,
        (void *)&complete, /* Data sent as is to the callback function*/
        pOpData, /* Operational data struct */
        pBufferList, /* Source buffer list */
        pBufferList, /* Same src & dst for an in-place operation*/
        NULL);
    
  • Wait for Completion: Because the asynchronous API is used in this example, the callback function must be handled. The below example uses a macro that can be defined differently for different operating systems. In a typical real-world application, the calling thread would not block, and the callback would essentially re-inject the (decrypted, decapsulated) packet into the stack.

    if (!COMPLETION_WAIT(&complete, TIMEOUT_MS)) {
        PRINT_ERR("timeout or interruption in cpaCySymPerformOp\n");
        status = CPA_STATUS_FAIL;
    }
    

Note

In a normal usage scenario, the session would be reused multiple times to encrypt multiple buffers or packets. In this example, however, the session is torn down.

Since cryptographic API v2.2, before removing the symmetric session context it is recommended to wait for the completion of any outstanding request using cpaCySymSessionInUse. It is executed in the symSessionWaitForInflightReq call which polls for the in-flight requests.

symSessionWaitForInflightReq(sessionCtx)

Finally, clean up by freeing up memory, stopping the instance, etc. See below example.

sessionStatus = cpaCySymRemoveSession(cyInstHandle, sessionCtx);

Query statistics at this point, which can be useful for debugging. Some implementations may also make the statistics available through other mechanisms, such as the /proc virtual filesystem.

Since cryptographic API v2.2, two new functions have been implemented: cpaCySymUpdateSession and cpaCySymSessionInUse.

  • The function cpaCySymUpdateSession can be used to update certain parameters of a session like the cipher key, the cipher direction, and the authentication key. cpaCySymSessionInUse indicates whether there are outstanding requests on a given session.

  • As a result of the implementation of this feature, the behavior of cpaCySymRemoveSession has been changed. cpaCySymRemoveSession will fail if there are outstanding request for the session that the user is trying to remove. As a result, it is recommended to wait for the completion of any outstanding request, using cpaCySymSessionInUse, before removing a session.

Hash

This example demonstrates the usage of the symmetric API, specifically using this API to perform a hash operation. It performs a SHA-256 hash operation on some sample data.

These samples are located in /sym/hash_sample.

Note

This hash example is very similar to the cipher example, so only the differences are highlighted.

When creating and initializing a session, some of the fields of the session initialization operational data structure are different from the cipher case, as shown below.

/* Populate symmetric session data structure for a plain hash operation */
sessionSetupData.sessionPriority = CPA_CY_PRIORITY_NORMAL;
sessionSetupData.symOperation = CPA_CY_SYM_OP_HASH;
sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA256;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;
/* Place the digest result in a buffer unrelated to srcBuffer */
sessionSetupData.digestIsAppended = CPA_FALSE;
/* Generate the digest */
sessionSetupData.verifyDigest = CPA_FALSE;

When calling the function to perform the hash operation, some of the fields of the operational data structure are again different from the cipher case, as shown below.

pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->hashStartSrcOffsetInBytes = 0;
pOpData->messageLenToHashInBytes = sizeof(vectorData);
pOpData->pDigestResult = pDigestBuffer;

Hash a File

This example demonstrates the usage of the symmetric API for partial mode, specifically using this API to perform hash operations. It performs a SHA-1 hash operation on a file.

These samples are located in /sym/hash_file_sample.

Note

This hash example is very similar to the cipher example, so only the differences are highlighted.

When creating and initializing a session, some of the fields of the session initialization operational data structure are different from the cipher case, as shown below.

/* Populate symmetric session data structure for a plain hash operation */
sessionSetupData.sessionPriority = CPA_CY_PRIORITY_NORMAL;
sessionSetupData.symOperation = CPA_CY_SYM_OP_HASH;
sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA1;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;
/* Place the digest result in a buffer unrelated to srcBuffer */
sessionSetupData.digestIsAppended = CPA_FALSE;
/* Generate the digest */
sessionSetupData.verifyDigest = CPA_FALSE;

Memory is allocated for the source buffer in a similar way to the cipher case.

To perform the operation, data is read from the file to the source buffer and the symmetric API is called repeatedly with packetType set to CPA_CY_SYM_PACKET_TYPE_PARTIAL. When the end of the file is reached the API is called with packetType set to CPA_CY_SYM_PACKET_TYPE_PARTIAL_LAST. The digest is produced only on the last call to the API. See example below.

while (!feof(srcFile)) {
    /* Read from file into src buffer */
    pBufferList->pBuffers->dataLenInBytes = fread(pSrcBuffer, 1, SAMPLE_BUFF_SIZE, srcFile);

    /* If we have reached the end of file set the last partial flag */
    if (feof(srcFile)) {
        pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_LAST_PARTIAL;
    } else {
        pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_PARTIAL;
    }

    pOpData->sessionCtx = sessionCtx;
    pOpData->hashStartSrcOffsetInBytes = 0;
    pOpData->messageLenToHashInBytes = pBufferList->pBuffers->dataLenInBytes;
    pOpData->pDigestResult = pDigestBuffer;

    PRINT_DBG("cpaCySymPerformOp\n");
    /* Perform symmetric operation */
    status = cpaCySymPerformOp(
        cyInstHandle,
        (void *)&complete, /* Data sent as is to the callback function */
        pOpData, /* Operational data struct */
        pBufferList, /* Source buffer list */
        pBufferList, /* Same src & dst for an in-place operation */
        NULL);

    if (CPA_STATUS_SUCCESS != status) {
        PRINT_ERR("cpaCySymPerformOp failed. (status = %d)\n", status);
        break;
    }

    if (CPA_STATUS_SUCCESS == status) {
        /* Wait until the completion of the operation */
        if (!COMPLETION_WAIT((&complete), TIMEOUT_MS)) {
            PRINT_ERR("timeout or interruption in cpaCySymPerformOp\n");
            status = CPA_STATUS_FAIL;
            break;
        }
    }
}

Chained Cipher and Hash

This example demonstrates the usage of the symmetric API, specifically using this API to perform a chained cipher and hash operation. It encrypts some sample text using the AES-256 algorithm in CBC mode, and then performs a SHA-256 Hashed Message Authenticate Code (HMAC) operation on the ciphertext, writing the Message Authentication Code (MAC) to the buffer immediately after the ciphertext.

These samples are located in /sym/alg_chaining_sample.

Note

This chained example is very similar to the cipher and hash examples, so only the differences are highlighted.

When creating and initializing a session, some of the fields of the session initialization operational data structure are different, as shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA256;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.authKey = sampleCipherKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = sizeof(sampleCipherKey);

/* The resulting MAC is to be placed immediately after the ciphertext */
sessionSetupData.digestIsAppended = CPA_TRUE;
sessionSetupData.verifyDigest = CPA_FALSE;

When calling the function to perform the chained cipher and hash operation, some of the fields of the operational data structure are again different from the cipher case, as shown below.

/* Populate the structure containing the operational data that is
 * needed to run the algorithm */
pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->pIv = pIvBuffer;
pOpData->ivLenInBytes = sizeof(sampleCipherIv);
pOpData->cryptoStartSrcOffsetInBytes = 0;
pOpData->hashStartSrcOffsetInBytes = 0;
pOpData->messageLenToCipherInBytes = sizeof(sampleAlgChainingSrc);
pOpData->messageLenToHashInBytes = sizeof(sampleAlgChainingSrc);

Notice the digestIsAppended is set in the session; therefore, the MAC is placed immediately after the region to hash, and the pDigestResult parameter of the operational data is ignored.

Chained Cipher and Hash – IPSec Like Use Case

This example demonstrates the usage of the symmetric API for IPSec-like use cases, as described in the below figures. For the outbound direction, this example uses the symmetric API to perform a chained cipher and hash operation. It encrypts some plaintext using the Advanced Encryption Standard (AES) algorithm in CBC mode, and then performs a SHA1 HMAC operation on the ciphertext, initialization vector, and header, writing the Integrity Check Value (ICV) to the buffer immediately after the ciphertext. For the inbound direction, this example again uses the symmetric API to perform a chained hash and cipher operation. It performs a SHA1 HMAC operation on the ciphertext, initialization vector, and header and compares the result with the input ICV. Then it decrypts the ciphertext using the AES algorithm in CBC mode.

../_images/IPSec_Outbound.png
../_images/IPSec_Inbound.png

These samples are located in /sym/ipsec_sample.

Note

This chained example is very similar to the cipher and hash examples, so only the differences are highlighted.

When creating and initializing a session in the outbound direction, the session initialization operational data structure is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA1;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = ICV_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.authKey = sampleAuthKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = sizeof(sampleAuthKey);

/* Even though ICV follows immediately after the region to hash
 * digestIsAppended is set to false in this case to workaround
 * errata number IXA00378322 */
sessionSetupData.digestIsAppended = CPA_FALSE;
/* Generate the ICV in outbound direction */
sessionSetupData.verifyDigest = CPA_FALSE;

When calling the function to perform the chained cipher and hash operation, the fields of the operational data structure are shown below.

/* Populate the structure containing the operational data that is
 * needed to run the algorithm in outbound direction */
pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->pIv = pIvBuffer;
pOpData->ivLenInBytes = sizeof(sampleCipherIv);
pOpData->cryptoStartSrcOffsetInBytes = sizeof(sampleEspHdrData) + sizeof(sampleCipherIv);
pOpData->messageLenToCipherInBytes = sizeof(samplePayload);
pOpData->hashStartSrcOffsetInBytes = 0;
pOpData->messageLenToHashInBytes = sizeof(sampleEspHdrData) + sizeof(sampleCipherIv) + sizeof(samplePayload);
/* Even though ICV follows immediately after the region to hash
 * digestIsAppended is set to false in this case to workaround
 * errata number IXA00378322 */
pOpData->pDigestResult = pSrcBuffer + (sizeof(sampleEspHdrData) + sizeof(sampleCipherIv) + sizeof(samplePayload));

In this example samplePayload is the packet data plus the Encapsulating Security Payload (ESP) trailer.

When creating and initializing a session in the inbound direction, the session initialization operational data structure is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA1;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = ICV_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.authKey = sampleAuthKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = sizeof(sampleAuthKey);

/* ICV follows immediately after the region to hash */
sessionSetupData.digestIsAppended = CPA_TRUE;
/* Verify the ICV in the inbound direction */
sessionSetupData.verifyDigest = CPA_TRUE;

When calling the function to perform the chained hash and cipher operation, the fields of the operational data structure are listed below.

/* Populate the structure containing the operational data that is
 * needed to run the algorithm in inbound direction */
pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->pIv = pIvBuffer;
pOpData->ivLenInBytes = sizeof(sampleCipherIv);
pOpData->cryptoStartSrcOffsetInBytes = sizeof(sampleEspHdrData) + sizeof(sampleCipherIv);
pOpData->messageLenToCipherInBytes = bufferSize - (sizeof(sampleEspHdrData) + sizeof(sampleCipherIv) + ICV_LENGTH);
pOpData->hashStartSrcOffsetInBytes = 0;
pOpData->messageLenToHashInBytes = bufferSize - ICV_LENGTH;

In the example above, bufferSize is the size of the data input (header, iv, ciphertext, and ICV).

Chained Cipher and Hash – SSL Like Use Case

This example demonstrates the usage of the symmetric API for SSL-like use cases, as described in the below figures. For the outbound direction, this example employs the symmetric API to perform a chained hash and cipher operation. It performs a SHA1 HMAC2 operation on a sequence number, part of the header, and the plaintext.

The resultant MAC is placed immediately after the plaintext. Then it encrypts the plaintext, MAC, and padding using the AES algorithm in CBC mode.

Note

Not all SSL use cases use HMAC. For example, the Secure Sockets Layer (SSL) Protocol Version 3.0 (SSLv3) (RFC 6106) does not use HMAC (in this case the nested hash functionality on the API can be used). However, the Transport Layer Security (TLS) Protocol (TLS V1.2), for example, does use the HMAC algorithm.

For the inbound direction, this example again employs the use of the symmetric API to perform a chained cipher and hash operation. It decrypts the ciphertext using the AES algorithm in CBC mode. Then it performs a SHA1 HMAC operation on the resultant plaintext, sequence number, and part of the header and compares the result with the input MAC.

Note

For the inbound direction to use the chained API, the length of the plaintext needs to be known before the ciphertext is decrypted to set messageLenToHashInBytes and the length field in the header correctly.

For stream ciphers (e.g., ARC4), there is no padding added in the outbound direction, so the length of the plaintext is simply the length of the ciphertext minus the length of the MAC. However, for block ciphers in CBC mode (as used in this example), the padlen is required to calculate the plaintext length. The final block of the ciphertext needs to be decrypted to discover the padlen. In this example, before calling the chained API, the final block of the ciphertext is decrypted to discover the padlen.

../_images/SSL_Outbound.png
../_images/SSL_Inbound.png

If using a block cipher in CBC mode, then the last ciphertext block is used as the IV for subsequent packets (or records) in Secure Sockets Layer (SSL) and TLSv1.0, whereas in TLSv1.1 and 1.2 an explicit IV is used. However, if using a stream cipher that does not use a synchronization vector (such as ARC4), the stream cipher state from the end of one packet is used to process the subsequent packets. If using the QA API in this case, then partial mode should be used to ensure the stream cipher state is maintained across multiple calls to the API.

Note

This chained example is very similar to the cipher and hash examples, so only the differences are highlighted.

When creating and initializing a session in the outbound direction, the session setup data structure is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA1;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = MAC_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.authKey = sampleAuthKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = sizeof(sampleAuthKey);

/* MAC follows immediately after the region to hash */
sessionSetupData.digestIsAppended = CPA_TRUE;
/* Generate the MAC in outbound direction */
sessionSetupData.verifyDigest = CPA_FALSE;

A buffer large enough to hold the plaintext, MAC and padding is required. The size of this buffer will be the one shown in the below example.

bufferSize = sizeof(samplePayload) + MAC_LENGTH;

/* bufferSize needs to be rounded up to a multiple of the AES block size */
padLen = 16 - bufferSize % 16;
bufferSize += padLen;
/* padLen excludes pad_length field */
padLen--;

This buffer is filled with plaintext and padding leaving room for the chained API operation to add the MAC, as shown below.

memcpy(pSrcBuffer, samplePayload, sizeof(samplePayload));
/* Leave space for MAC but insert padding data */
for (i = 0; i <= padLen; i++) {
  pSrcBuffer[(sizeof(samplePayload) + MAC_LENGTH + i)] = padLen;
}

The session sequence number, the header and the buffer with the plaintext are described using a CpaBufferList, as shown below.

pBufferList->pBuffers = pFlatBuffer;
pBufferList->numBuffers = numBuffers;
pBufferList->pPrivateMetaData = pBufferMeta;

/* Seq number */
pFlatBuffer->dataLenInBytes = SSL_CombinedHeadSize;
pFlatBuffer->pData = pCombinedHeadBuffer;
pFlatBuffer++;
memcpy((char *)pCombinedHeadBuffer + SESSION_SEQ_START, &sessSeqNum, sizeof(sessSeqNum));
memcpy((char *)pCombinedHeadBuffer + HDR_START, sampleHdrData, sizeof(sampleHdrData));

/* Data */
pFlatBuffer->dataLenInBytes = bufferSize;
pFlatBuffer->pData = pSrcBuffer;

When calling the function to perform the chained hash and cipher operation, the fields of the operational data structure are shown below.

pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->pIv = pIvBuffer;
pOpData->ivLenInBytes = sizeof(sampleCipherIv);
pOpData->cryptoStartSrcOffsetInBytes = SSL_CombinedHeadSize;
pOpData->messageLenToCipherInBytes = bufferSize;
pOpData->hashStartSrcOffsetInBytes = SESSION_SEQ_START;
pOpData->messageLenToHashInBytes = sizeof(sessSeqNum) + sizeof(sampleHdrData) + bufferSize - MAC_LENGTH - padLen;

When creating and initializing a session in the inbound direction, the session setup data structure is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA1;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = MAC_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.authKey = sampleAuthKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = sizeof(sampleAuthKey);

/* MAC follows immediately after the region to hash */
sessionSetupData.digestIsAppended = CPA_TRUE;
/* Generate the MAC in outbound direction */
sessionSetupData.verifyDigest = CPA_FALSE;

In this case the length of the ciphertext is bufferSize to calculate the padLen the final block is decrypted. See example below.

Cpa8U resBuff[16];

/* For decrypt direction need to decrypt the final block
 * to determine the messageLenToHashInBytes */
status = sampleCodeAesCbcDecrypt(
  sampleCipherKey,
  sizeof(sampleCipherKey),
  (pSrcBuffer + (bufferSize - 32)), /* IV */
  (pSrcBuffer + (bufferSize - 16)), /* src */
  resBuff); /* dest */

/* padLen is the last byte decrypted incremented by one to
 * included the padLen block itself */
padLen = resBuff[15] + 1;

When calling the function to perform the chained cipher and hash operation, the fields of the operational data structure are shown below.

pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->pIv = pIvBuffer;
pOpData->ivLenInBytes = sizeof(sampleCipherIv);
pOpData->cryptoStartSrcOffsetInBytes = SSL_CombinedHeadSize;
pOpData->messageLenToCipherInBytes = bufferSize;
pOpData->hashStartSrcOffsetInBytes = SESSION_SEQ_START;
pOpData->messageLenToHashInBytes = sizeof(sessSeqNum) + sizeof(sampleHdrData) + bufferSize - MAC_LENGTH - padLen;

Chained Cipher and Hash – CCM Use Case

This example demonstrates the usage of the symmetric API to perform a CCM operation as described in NIST publication SP800-38C (Recommendation for Block Cipher Modes of Operation: The CCM Mode for Authentication and Confidentiality). Refer to Related Documents and References for more information.

This sample is located in /sym/ccm_sample.

Note

This chained example is very similar to the cipher and hash examples, so only the differences are highlighted.

For the generation-encryption process the session setup data is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CCM;
sessionSetupData.cipherSetupData.pCipherKey = sampleKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_AES_CCM;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;

/* Notice for CCM authKey and authKeyLen are not required this
 * information is provided by the cipherKey in cipherSetupData */
sessionSetupData.hashSetupData.authModeSetupData.aadLenInBytes = sizeof(sampleAssocData);
/* For CCM digestAppended and digestVerify are not required. In
 * the encrypt direction digestAppended is CPA_TRUE and
 * digestVerify is CPA_FALSE */

For the decryption-verification process the session setup data is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CCM;
sessionSetupData.cipherSetupData.pCipherKey = sampleKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_AES_CCM;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.aadLenInBytes = sizeof(sampleAssocData);

The IV and AAD buffers are allocated as shown below.

/* Allocate memory to store IV. For CCM this is the counter block
 * ctr0 (size equal to AES block size). The implementation will
 * construct the ctr0 block given the nonce. Space for ctr0 must be
 * allocated here */
status = PHYS_CONTIG_ALLOC(&pIvBuffer, AES_BLOCK_SIZE);

if (CPA_STATUS_SUCCESS == status) {
  /* Allocate memory for AAD. For CCM this memory will hold the 16 byte
   * B0 block, the 2 bytes encoded length of associated data, the
   * associated data itself and any padding to ensure total size is
   * a multiple of the AES block size */
  aadBuffSize = B0_BLOCK_SIZE + ALEN_ENCODING_SIZE + sizeof(sampleAssocData);
  if (aadBuffSize % AES_BLOCK_SIZE) {
    aadBuffSize += AES_BLOCK_SIZE - (aadBuffSize % AES_BLOCK_SIZE);
  }
  status = PHYS_CONTIG_ALLOC(&pAadBuffer, aadBuffSize);
}

The operational data needed to perform the generate-encrypt or decrypt-verify operation is shown below.

pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->pIv = pIvBuffer;
/* Even though the iv buffer is 16 bytes the ivLenInBytes
 * is set to the length of the nonce. For CCM valid lengths
 * are in the range 7-13 */
pOpData->ivLenInBytes = sizeof(sampleNonce);
pOpData->cryptoStartSrcOffsetInBytes = 0;
pOpData->messageLenToCipherInBytes = sizeof(samplePayload);
/* Notice for CCM hash offset and length are not required */
pOpData->pAdditionalAuthData = pAadBuffer;

/* Populate pIv and pAdditionalAuthData buffers with
 * nonce and assoc data */
CPA_CY_SYM_CCM_SET_NONCE(pOpData, sampleNonce, sizeof(sampleNonce));
CPA_CY_SYM_CCM_SET_AAD(pOpData, sampleAssocData, sizeof(sampleAssocData));

Chained Cipher and Hash – GCM Use Case

This example demonstrates the usage of the symmetric API to perform a GCM operation as described in NIST publication SP800-38D (Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC). Refer to Related Documents and References for more information.

These samples are located in /sym/gcm_sample.

An example of the session setup data and operational data for GCM authenticated encryption and decryption is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_GCM;
sessionSetupData.cipherSetupData.pCipherKey = sampleKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_AES_GCM;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = TAG_LENGTH;

/* For GCM authKey and authKeyLen are not required this information
 * is provided by the cipherKey in cipherSetupData */
sessionSetupData.hashSetupData.authModeSetupData.aadLenInBytes = sizeof(sampleAddAuthData);
/* Tag follows immediately after the region to hash */
sessionSetupData.digestIsAppended = CPA_TRUE;
/* digestVerify is not required to be set. For GCM authenticated
 * encryption this value is understood to be CPA_FALSE */

For authenticated encryption the session setup data is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_GCM;
sessionSetupData.cipherSetupData.pCipherKey = sampleKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_AES_GCM;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = TAG_LENGTH;

/* For GCM authKey and authKeyLen are not required this information
 * is provided by the cipherKey in cipherSetupData */
sessionSetupData.hashSetupData.authModeSetupData.aadLenInBytes = sizeof(sampleAddAuthData);
/* Tag follows immediately after the region to hash */
sessionSetupData.digestIsAppended = CPA_TRUE;
/* digestVerify is not required to be set. For GCM authenticated
 * decryption this value is understood to be CPA_TRUE */

For authenticated decryption the session setup data is shown below.

sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_GCM;
sessionSetupData.cipherSetupData.pCipherKey = sampleKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_AES_GCM;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = TAG_LENGTH;

/* For GCM authKey and authKeyLen are not required this information
 * is provided by the cipherKey in cipherSetupData */
sessionSetupData.hashSetupData.authModeSetupData.aadLenInBytes = sizeof(sampleAddAuthData);
/* Tag follows immediately after the region to hash */
sessionSetupData.digestIsAppended = CPA_TRUE;
/* digestVerify is not required to be set. For GCM authenticated
 * decryption this value is understood to be CPA_TRUE */

The IV and AAD buffers are allocated as shown below.

/* Allocate memory to store IV. For GCM this is the block J0
 * (size equal to AES block size). If iv is 12 bytes the
 * implementation will construct the J0 block given the iv.
 * If iv is not 12 bytes then the user must construct the J0
 * block and give this as the iv. In both cases space for J0
 * must be allocated. */
status = PHYS_CONTIG_ALLOC(&pIvBuffer, AES_BLOCK_SIZE);

if (CPA_STATUS_SUCCESS == status) {
  /* Allocate memory for AAD. For GCM this memory will hold the
   * additional authentication data and any padding to ensure total
   * size is a multiple of the AES block size */
  aadBuffSize = sizeof(sampleAddAuthData);
  if (aadBuffSize % AES_BLOCK_SIZE) {
    aadBuffSize += AES_BLOCK_SIZE - (aadBuffSize % AES_BLOCK_SIZE);
  }
  status = PHYS_CONTIG_ALLOC(&pAadBuffer, aadBuffSize);
}

The operational data needed to perform the encrypt or decrypt operation is shown below.

pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->pIv = pIvBuffer;
/* In this example iv is 12 bytes. The implementation
 * will use the iv to generation the J0 block */
memcpy(pIvBuffer, sampleIv, sizeof(sampleIv));
pOpData->ivLenInBytes = sizeof(sampleIv);
pOpData->cryptoStartSrcOffsetInBytes = 0;
pOpData->messageLenToCipherInBytes = sizeof(samplePayload);
/* For GCM hash offset and length are not required */
pOpData->pAdditionalAuthData = pAadBuffer;

Note

GMAC is supported using the same API and similar data structures as the general GCM case shown above. However, for GMAC, the messageLenToCipherInBytes will be set to 0.

Chained Cipher and Hash Using the Symmetric Data Plane API

This example demonstrates the usage of the data plane symmetric API to perform a chained cipher and hash operation. It encrypts some sample text using the AES-256 algorithm in CBC mode, and then performs an SHA-256 HMAC operation on the ciphertext, writing the MAC to the buffer immediately after the ciphertext.

This example has been simplified to demonstrate the basics of how to use the API and build the structures required. This example does not demonstrate the optimal way to use the API to get the maximum performance for a particular implementation. Refer to Related Documents and References for implementation specific documentation (for example, the Intel® Communications Chipset 8900 to 8920 Series Software Programmer’s Guide) and performance sample code for a guide on how to use the API for best performance.

These samples are located in /sym/symdp_sample.

Use of the data plane symmetric API follows some of the same basic steps as the traditional symmetric API:

  • Discover and start up the cryptographic service instance.

  • Register a callback function for the instance.

  • Create and initialize a session.

  • Enqueue the symmetric operation on the instance.

  • Submit the symmetric operation for processing.

  • Poll the instance for a response.

  • Tear down the session.

  • Stop the cryptographic service instance.

Cryptographic service instances are discovered and started in the same way and using the same API as the traditional symmetric use cases described in the cipherSample section.

The next step is to register a callback function for the cryptographic instance. The function is called back in the context of the polling function when an asynchronous operation has completed. This function can perform whatever processing is appropriate to the application.

Callback differs from the traditional symmetric API, where the callback function is registered for the session. See below.

status = cpaCySymDpRegCbFunc(cyInstHandle, symDpCallback);

Create and initialize a session is shown below.

sessionSetupData.sessionPriority = CPA_CY_PRIORITY_HIGH;
sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA256;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.authKey = sampleCipherKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = sizeof(sampleCipherKey);

/* Even though MAC follows immediately after the region to hash
 * digestIsAppended is set to false in this case to workaround
 * errata number IXA00378322 */
sessionSetupData.digestIsAppended = CPA_FALSE;
sessionSetupData.verifyDigest = CPA_FALSE;

/* Determine size of session context to allocate */
PRINT_DBG("cpaCySymDpSessionCtxGetSize\n");
status = cpaCySymDpSessionCtxGetSize(cyInstHandle, &sessionSetupData, &sessionCtxSize);

if (CPA_STATUS_SUCCESS == status) {
  /* Allocate session context */
  status = PHYS_CONTIG_ALLOC(&sessionCtx, sessionCtxSize);
}

if (CPA_STATUS_SUCCESS == status) {
  /* Initialize the session */
  PRINT_DBG("cpaCySymDpInitSession\n");
  status = cpaCySymDpInitSession(cyInstHandle, &sessionSetupData, sessionCtx);
}

#ifdef LAC_HW_PRECOMPUTES
if (CPA_STATUS_SUCCESS == status) {
  /* Poll for hw pre-compute responses */
  do {
    status = icp_sal_CyPollDpInstance(cyInstHandle, 0);
  } while (CPA_STATUS_SUCCESS != status);
}
#endif

In this example, data is stored in flat buffers (as opposed to scatter-gather lists). The operational data in this case is shown below.

sessionSetupData.sessionPriority = CPA_CY_PRIORITY_HIGH;
sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_AES_CBC;
sessionSetupData.cipherSetupData.pCipherKey = sampleCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = sizeof(sampleCipherKey);
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA256;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;
sessionSetupData.hashSetupData.authModeSetupData.authKey = sampleCipherKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = sizeof(sampleCipherKey);

/* Even though MAC follows immediately after the region to hash
 * digestIsAppended is set to false in this case to workaround
 * errata number IXA00378322 */
sessionSetupData.digestIsAppended = CPA_FALSE;
sessionSetupData.verifyDigest = CPA_FALSE;

/* Determine size of session context to allocate */
PRINT_DBG("cpaCySymDpSessionCtxGetSize\n");
status = cpaCySymDpSessionCtxGetSize(cyInstHandle, &sessionSetupData, &sessionCtxSize);

if (CPA_STATUS_SUCCESS == status) {
  /* Allocate session context */
  status = PHYS_CONTIG_ALLOC(&sessionCtx, sessionCtxSize);
}

if (CPA_STATUS_SUCCESS == status) {
  /* Initialize the session */
  PRINT_DBG("cpaCySymDpInitSession\n");
  status = cpaCySymDpInitSession(cyInstHandle, &sessionSetupData, sessionCtx);
}
#ifdef LAC_HW_PRECOMPUTES
if (CPA_STATUS_SUCCESS == status) {
  /* Poll for hw pre-compute responses */
  do {
    status = icp_sal_CyPollDpInstance(cyInstHandle, 0);
  } while (CPA_STATUS_SUCCESS != status);
}
#endif

This request is then enqueued on the instance as shown below.

status = cpaCySymDpEnqueueOp(pOpData, CPA_FALSE);

Other requests can now be enqueued before submitting all the requests to be processed. Enqueued requests allow the cost of submitting a request (which can be expensive, in terms of cycles, for some hardware-based implementations) to be amortized over all enqueued requests on the instance. Once sufficient requests have been enqueued they are all submitted for processing. See below.

status = cpaCySymDpPerformOpNow(cyInstHandle);

An alternative to calling the cpaCySymDpPerformOpNow function is to set performOpNow to CPA_TRUE when calling the enqueue functions (cpaCySymDpEnqueueOp or cpaCySymDpEnqueueOpBatch). This is illustrated in the section Data Compression Data Plane API.

After submitting several requests and possibly doing other work (e.g., enqueuing and submitting more requests), the application can poll for responses that invoke the callback function registered with the instance. Refer to Related Documents and References for implementation specific documentation for information on the implementations of polling functions.

Once all requests associated with a session have been completed, the session can be removed as shown below.

sessionStatus = cpaCySymDpRemoveSession(cyInstHandle, sessionCtx);

Since cryptographic API v2.2, before removing the symmetric session context, it is recommended to wait for the completion of any outstanding request using cpaCySymSessionInUse. It is executed in the symSessionWaitForInflightReq call, which polls for the in-flight requests.

symSessionWaitForInflightReq(sessionCtx)

Since Cryptographic API v2.2, two new functions have been implemented: cpaCySymUpdateSession and cpaCySymSessionInUse.

  • The function cpaCySymUpdateSession can be used to update certain parameters of session like a cipher key, the cipher direction, and the authentication key. cpaCySymSessionInUse indicates whether there are outstanding requests on a given session.

  • As a result of the implementation of this feature, the behavior of cpaCySymRemoveSession has been changed. The cpaCySymRemoveSession fails if there is an outstanding request for the session that the user is trying to remove. As a result, it is recommended to wait for the completion of any outstanding request, using cpaCySymSessionInUse, before removing a session.

TLS Key and MGF Mask Generation

Note

Refer to Related Documents and References for the API manuals for full details of Key and Mask Generation operations.

  1. Define a Flat Buffer callback function as per the API prototype. If synchronous operation is preferred, instead simply pass NULL to the API for the callback parameter.

  2. Allocate memory for the operation.

  3. Populate data for the appropriate operation data structure as per the API prototype.

    1. Fill in the Flat Buffers, a pointer to data, and length.

    2. Fill in the options for the operation required.

  4. Call the appropriate Key or Mask Generation API.

  5. Complete the operation.

The API for TLS key operations is based on the Transport Layer Security (TLS) Protocol Version 1.1 standard, RFC 4346. Backward compatibility is supported with the legacy Transport Layer Security (TLS) Protocol Version 1.0 standard, RFC 2246. The user-defined label should be used for backward compatibility with the client write key, server write key, and iv block. Refer to Related Documents and References (Intel® QAT Cryptographic API Reference Manual) for details of populating CpaCyKeyGenTlsOpData, the operation data structure.

The following sections describe examples of the parameter mapping to the cryptographic API.

Setting CpaCyKeyGenTlsOpData Structure Fields

The Transport Layer Security (TLS) Protocol Version 1.1 standard, RFC 4346 (refer to Related Documents and References), section 6.3 key_block is described as:

key_block = PRF(SecurityParameters.master_secret, "key expansion", SecurityParameters.server_random + SecurityParameters.client_random);

This maps to the cryptographic APIs CpaCyKeyGenTlsOpData as follows:

TLS Key-Material Derivation:

  tlsOp = CPA_CY_KEY_TLS_OP_KEY_MATERIAL_DERIVE
  secret = master secret key
  seed = server_random + client_random
  userLabel = NULL

Setting the CpaCyKeyGenTlsOpData structure fields for backward compatibility:

  1. In the Transport Layer Security (TLS) Protocol Version 1.0 standard, RFC 2246 (refer to Related Documents and References), Section 6.3 final_client_write_key is described as:

    final_client_write_key = PRF(client_write_key, "client write key", client_random + server_random)[0..15]
    

    This maps to the cryptographic APIs CpaCyKeyGenTlsOpData as follows:

    TLS User Defined Derivation:
    
      tlsOp = CPA_CY_KEY_TLS_OP_USER_DEFINED
      secret = client_write_key
      seed = client_random + server_random
      userLabel = "client write key"
    
  2. In the Transport Layer Security (TLS) Protocol v1.0 standard, RFC 2246 (refer to Related Documents and References), Section 6.3 final_server_write_key is described as:

    final_server_write_key = PRF(server_write_key, "server write key", client_random + server_random)[0..15]
    

    This maps to the cryptographic APIs CpaCyKeyGenTlsOpData as follows:

    TLS User Defined Derivation:
    
      tlsOp = CPA_CY_KEY_TLS_OP_USER_DEFINED
      secret = server_write_key
      seed = client_random + server_random
      userLabel = "server write key"
    
  3. In the Transport Layer Security (TLS) Protocol Version 1.0 standard, RFC 2246 (refer to Related Documents and References), Section 6.3 iv_block is described as:

    iv_block = PRF("", "IV block", client_random + server_random)[0..15]
    

    This maps to the cryptographic APIs CpaCyKeyGenTlsOpData as follows:

    TLS User Defined Derivation:
    
      tlsOp = CPA_CY_KEY_TLS_OP_USER_DEFINED
      secret = NULL
      seed = client_random + server_random
      userLabel = "IV block"
    

Memory for the user label must be physically contiguous memory allocated by the user. This memory must be available to the API for the duration of the operation.

Session Update for Chained Cipher and Hash Operation

This example demonstrates the usage of the session update together with data plane symmetric API to perform a chained cipher and hash operation. It performs a KASUMI F9 hash operation on the sample text and then encrypts the sample text using the KASUMI F8 algorithm. After the operation is complete, the cipher and authentication keys are updated inside the session and the operation is performed again with different keys.

Note

This example is simplified to demonstrate the basics of how to use the API and how to build the structures required. This example does not demonstrate the optimal way to use the API to get maximum performance for a particular implementation. Refer to Related Documents and References for implementation specific documentation and performance sample code for a guide on how to use the API for best performance.

These samples are located in /sym/update_sample.

The following are the details of the steps performed in the sample:

  • Cryptographic service instances are discovered and started in the same way and using the same API as the traditional symmetric use cases described in the cipherSample section.

  • Next, register a callback function for the cryptographic instance. The function is called back in the context of the polling function when an asynchronous operation has completed. This function can perform whatever processing is appropriate to the application. Note this differs from the traditional symmetric API where the callback function is registered for the session.

    status = cpaCySymDpRegCbFunc(cyInstHandle, symDpCallback);
    

Create and Initialize a Session

sessionSetupData.sessionPriority = CPA_CY_PRIORITY_HIGH;
sessionSetupData.symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
sessionSetupData.algChainOrder = CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;

sessionSetupData.cipherSetupData.cipherAlgorithm = CPA_CY_SYM_CIPHER_KASUMI_F8;
sessionSetupData.cipherSetupData.pCipherKey = pCipherKey;
sessionSetupData.cipherSetupData.cipherKeyLenInBytes = cipherKeyLen;
sessionSetupData.cipherSetupData.cipherDirection = CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;

sessionSetupData.hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_KASUMI_F9;
sessionSetupData.hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
sessionSetupData.hashSetupData.digestResultLenInBytes = DIGEST_LENGTH;

sessionSetupData.hashSetupData.authModeSetupData.authKey = authKey;
sessionSetupData.hashSetupData.authModeSetupData.authKeyLenInBytes = authKeyLen;
sessionSetupData.hashSetupData.authModeSetupData.aadLenInBytes = sizeof(additionalAuthData);
sessionSetupData.digestIsAppended = CPA_TRUE;
sessionSetupData.verifyDigest = CPA_FALSE;

/* Determine size of session context to allocate */
PRINT_DBG("cpaCySymDpSessionCtxGetSize\n");
status = cpaCySymDpSessionCtxGetSize(cyInstHandle, &sessionSetupData, &sessionCtxSize);

if (CPA_STATUS_SUCCESS == status)
{
  /* Allocate session context */
  status = PHYS_CONTIG_ALLOC(sessionCtx, sessionCtxSize);
}

if (CPA_STATUS_SUCCESS == status)
{
  /* Initialize the session */
  PRINT_DBG("cpaCySymDpInitSession\n");
  status = cpaCySymDpInitSession(cyInstHandle, &sessionSetupData, *sessionCtx);
}

In this example, data is stored in flat buffers (as opposed to scatter-gather lists). The operational data in this case is the following:

pOpData->thisPhys = sampleVirtToPhys(pOpData);
pOpData->instanceHandle = cyInstHandle;
pOpData->sessionCtx = sessionCtx;
pOpData->pCallbackTag = (void *)0;
pOpData->cryptoStartSrcOffsetInBytes = 0;
pOpData->messageLenToCipherInBytes = srcLen;
pOpData->hashStartSrcOffsetInBytes = 0;
pOpData->messageLenToHashInBytes = srcLen;
pOpData->digestResult = sampleVirtToPhys(pSrcBuffer) + srcLen;
pOpData->iv = sampleVirtToPhys(pIvBuffer);
pOpData->pIv = pIvBuffer;
pOpData->ivLenInBytes = ivLen;
pOpData->additionalAuthData = sampleVirtToPhys(pAdditionalAuthData);
pOpData->pAdditionalAuthData = pAdditionalAuthData;
pOpData->srcBuffer = sampleVirtToPhys(pSrcBuffer);
pOpData->srcBufferLen = bufferSize;
pOpData->dstBuffer = sampleVirtToPhys(pDstBuffer);
pOpData->dstBufferLen = bufferSize;

This request is then enqueued on the instance as shown below.

status = cpaCySymDpEnqueueOp(pOpData, CPA_FALSE);

Other requests can now be enqueued before submitting all the requests to be processed. This allows the cost of submitting a request (which can be expensive, in terms of cycles, for some hardware-based implementations) to be amortized over all enqueued requests on the instance. Once sufficient requests have been enqueued they are all submitted for processing. See example below.

status = cpaCySymDpPerformOpNow(cyInstHandle);

An alternative to calling the cpaCySymDpPerformOpNow function is to set performOpNow to CPA_TRUE when calling the enqueue functions (cpaCySymDpEnqueueOp or cpaCySymDpEnqueueOpBatch). This is illustrated in the data compression data plane example.

After submitting a number of requests and possibly doing other work (e.g. enqueuing and submitting more requests) the application can poll for responses which will invoke the callback function registered with the instance. Refer to Related Documents and References for implementation specific documentation for information on the implementations polling functions.

After the operation is complete, the cipher key and authentication key are updated in the existing session via session update API as shown below.

sessionUpdateData.flags = CPA_CY_SYM_SESUPD_CIPHER_KEY;
sessionUpdateData.flags |= CPA_CY_SYM_SESUPD_AUTH_KEY;
sessionUpdateData.pCipherKey = pCipherKey;
sessionUpdateData.authKey = authKey;

status = cpaCySymUpdateSession(sessionCtx, &sessionUpdateData);

With the keys changed, the chained cipher and hash operation is performed again, just as described above.

Once all requests associated with a session have been completed, the session can be removed.

HKDF Use Case

This section contains sample code that demonstrates the usage of the symmetric API, specifically using this API to perform hash-based message authentication code key derivation function (HKDF) operations. It performs HKDF Extract and Expand, and Extract and Expand Label operation without and with sublabels (KEY and IV).

The simplified code example below is simplified and demonstrates how to use the API and build the structures required. This example does not demonstrate the optimal way to use the API to get maximum performance for implementation.

This sample is located in /quickassist/lookaside/access_layer/src/sample_code/functional/sym/hkdf_sample.

Instance Configuration and Memory Allocation

Cryptographic service instances are discovered and started in the same way and using the same API as the traditional symmetric use cases.

  1. If the instance is polled, start the polling thread. Polling is done in an implementation-dependent manner.

  2. Allocate memory for HKDF operation data:

    pOpData = qaeMemAllocNUMA(sizeof(CpaCyKeyGenHKDFOpData), instanceInfo2.nodeAffinity, BYTE_ALIGNMENT_64);
    

    This structure must be allocated with USDM to be pinned in physical memory.

  3. Allocate memory for HKDF output data. Output data is CpaFlatBuffer type:

    PHYS_CONTIG_ALLOC(&pHkdfData, hkdfDataSize);
    

HKDF Extract Expand Operation

To perform an Extract Expand operation, go to the CpaCyKeyGenHKDFOpData structure, and set hkdfKeyOp to CPA_CY_HKDF_KEY_EXTRACT_EXPAND.

Note

Provide the lengths seedLen, secretLen, and infoLen and copy all data into the seed, secret, and info tables.

pOpData->hkdfKeyOp = CPA_CY_HKDF_KEY_EXTRACT_EXPAND;

pOpData->seedLen = sizeof(ikm);
memcpy(pOpData->seed, ikm, pOpData->seedLen);

pOpData->secretLen = sizeof(slt);
memcpy(pOpData->secret, slt, pOpData->secretLen);

pOpData->infoLen = sizeof(inf);
memcpy(pOpData->info, inf, pOpData->infoLen);

HKDF Extract Expand Label Operation

To perform an Extract, Expand Label operation:

  1. Go to the CpaCyKeyGenHKDFOpData structure and set hkdfKeyOp to CPA_CY_HKDF_KEY_EXTRACT_EXPAND_LABEL.

  2. Provide the lengths seedLen, secretLen, and infoLen and copy all data into the seed, secret, and info tables.

  3. Set the number of labels in the numLabels field.

  4. Set label[0].labelLen.

  5. Copy label data into the label[0].label table.

  6. Finally, set the label[0].sublabelFlag field to 0x00 to disable generating sublabels.

pOpData->hkdfKeyOp = CPA_CY_HKDF_KEY_EXTRACT_EXPAND_LABEL;
pOpData->seedLen = sizeof(seed_label);
memcpy(pOpData->seed, seed_label, sizeof(seed_label));

pOpData->secretLen = sizeof(secret_label);
memcpy(pOpData->secret, secret_label, sizeof(secret_label));

pOpData->numLabels = 1;
memcpy(pOpData->label[0].label, label, sizeof(label));

pOpData->label[0].labelLen = sizeof(label);
pOpData->label[0].sublabelFlag = 0x00;

HKDF Extract Expand Label and Sublabels Operation

To perform an Extract, Expand Label and Sublabels operation:

  1. Go to the CpaCyKeyGenHKDFOpData structure and set hkdfKeyOp to CPA_CY_HKDF_KEY_EXTRACT_EXPAND_LABEL.

  2. Provide the lengths seedLen, secretLen, and infoLen and copy all data into the seed, secret, and info tables.

  3. Set the number of labels in the numLabels field.

  4. Set label[0].labelLen.

  5. Copy label data into the label[0].label table.

  6. Set the label[0].sublabelFlag and label[0].sublabelFlag field as shown below to generate Key and IV sublabels.

pOpData->hkdfKeyOp = CPA_CY_HKDF_KEY_EXTRACT_EXPAND_LABEL;

pOpData->seedLen = sizeof(seed_label);
memcpy(pOpData->seed, seed_label, sizeof(seed_label));

pOpData->secretLen = sizeof(secret_label);
memcpy(pOpData->secret, secret_label, sizeof(secret_label));

pOpData->numLabels = 1;
memcpy(pOpData->label[0].label, label, sizeof(label));

pOpData->label[0].labelLen = sizeof(label);
pOpData->label[0].sublabelFlag = CPA_CY_HKDF_SUBLABEL_KEY;
pOpData->label[0].sublabelFlag |= CPA_CY_HKDF_SUBLABEL_IV;

Perform HKDF Operation

The crypto instance must be specified in the instanceHandle to execute the HKDF operation. When the operation is performed asynchronously, the callback function and callback tag should be set in the pKeyGenCb and pCallbackTag arguments. Operational data is provided in pKeyGenTlsOpData, and CpaCyKeyHKDFCipherSuite must be chosen. The output is passed to the CpaFlatBuffer. All generated values are arranged one after the other in a single buffer. Depending on what operations are performed, the buffer length should be adjusted. See below example.

cpaCyKeyGenTls3(cyInstHandle, /* Instance handle */
                hkdfSampleCallback, /* Callback function */
                (void *)&complete, /* Callback tag */
                pOpData, /* HKDF operational data */
                CPA_CY_HKDF_TLS_AES_128_GCM_SHA256, /* HKDF cipher suite */
                &hkdfOut); /* Output buffer */

Using the Diffie-Hellman API

This example demonstrates the usage of the Diffie-Hellman API.

These samples are located in /asym/diffie_hellman_sample.

The following steps are carried out:

  • The example uses the API asynchronously; therefore, you must define a Diffie-Hellman callback function per the API prototype.

  • Instance, discovery, and start-up are made in a way similar to that defined for the symmetric examples above.

  • The function sampleDhPerformOp is called, which does the following:

    • Allocate memory for the operation and populate data for the appropriate DH phase 1 operation data structure to generate the public value. The fields to be allocated and populated are the primeP, the baseG, and the privateValueX. Space must also be allocated for the output, which is the public value (PV).

See below example to allocate memory and populate operational data.

status = OS_MALLOC(&pCpaDhOpDataP1, sizeof(CpaCyDhPhase1KeyGenOpData));

/*
 * Allocate input buffers for phase 1 and copy data. Input to DH
 * phase 1 includes the prime (primeP), the base g (baseG) and
 * a random private value (privateValueX) */
if (CPA_STATUS_SUCCESS == status) {
  memset(pCpaDhOpDataP1, 0, sizeof(CpaCyDhPhase1KeyGenOpData));

  pCpaDhOpDataP1->primeP.dataLenInBytes = sizeof(primeP_768);
  status = PHYS_CONTIG_ALLOC(&pCpaDhOpDataP1->primeP.pData, sizeof(primeP_768));

  if (NULL != pCpaDhOpDataP1->primeP.pData) {
    memcpy(pCpaDhOpDataP1->primeP.pData, primeP_768,
    sizeof(primeP_768));
  }
}

if (CPA_STATUS_SUCCESS == status) {
  pCpaDhOpDataP1->baseG.dataLenInBytes = sizeof(baseG1);
  status = PHYS_CONTIG_ALLOC(&pCpaDhOpDataP1->baseG.pData, sizeof(baseG1));

  if (NULL != pCpaDhOpDataP1->baseG.pData) {
    memcpy(pCpaDhOpDataP1->baseG.pData, baseG1, sizeof(baseG1));
  }
}

if (CPA_STATUS_SUCCESS == status) {
  pCpaDhOpDataP1->privateValueX.dataLenInBytes = sizeof(privateValueX);
  status = PHYS_CONTIG_ALLOC(&pCpaDhOpDataP1->privateValueX.pData, sizeof(privateValueX));

  if (NULL != pCpaDhOpDataP1->privateValueX.pData) {
    memcpy(pCpaDhOpDataP1->privateValueX.pData, privateValueX, sizeof(privateValueX));
  }
}

Invoke the phase 1 operation as shown below, which performs the modular exponentiation such that PV = (baseG ^ privateValueX) mod primeP.

Note

In the case of phase 1, the operation is invoked synchronously, hence the NULL pointer for the callback function.

status = cpaCyDhKeyGenPhase2Secret(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)asymCallback, /* CB function*/
    pCallbackTagPh2, /* Pointer to the complete variable */
    pCpaDhOpDataP2, /* Structure containing p, the public value & x */
    pOctetStringSecretKey); /* Private key (output of the function) */

In a real-world implementation of a key exchange protocol, the public value generated above would now be shared with another party, B. This example uses this public value to go on and invoke the second phase operation. First allocate memory for the secret value, setup the operational data for the phase 2 operation, and then perform that operation. This operation is invoked asynchronously, taking the callback function defined earlier as a parameter. See below example.

status = cpaCyDhKeyGenPhase2Secret(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)asymCallback, /* CB function */
    pCallbackTagPh2, /* Pointer to the complete variable */
    pCpaDhOpDataP2, /* Structure containing p, the public value & x*/
    pOctetStringSecretKey); /* Private key (output of the function) */

Finally, clean up by freeing up memory, stopping the instance, etc.

Prime Number Testing

This example demonstrates the usage of the prime number testing API.

These samples are located in /asym/prime_sample.

The following steps are carried out:

  • The API is used asynchronously, therefore, a callback function is defined as per the API prototype.

  • Instance, discovery, and start-up is made in a way similar to that defined for the symmetric examples above.

  • The function primePerformOp is called, which does the following:

    • Allocate memory for the operation.

    • Populate data for the appropriate input fields and perform the operation. The fields populated include the following:

      • Prime Candidate.

      • Whether to perform the greatest common divisor (GCD) test.

      • Whether to perform the Fermat test.

      • Number of Miller-Rabin rounds.

      • Whether to perform Lucas test.

pPrimeTestOpData->primeCandidate.pData = pPrime;
pPrimeTestOpData->primeCandidate.dataLenInBytes = sizeof(samplePrimeP_768);
pPrimeTestOpData->performGcdTest = CPA_TRUE;
pPrimeTestOpData->performFermatTest = CPA_TRUE;
pPrimeTestOpData->numMillerRabinRounds = NB_MR_ROUNDS;
pPrimeTestOpData->millerRabinRandomInput.pData = pMR;
pPrimeTestOpData->millerRabinRandomInput.dataLenInBytes = sizeof(MR);
pPrimeTestOpData->performLucasTest = CPA_TRUE;

status = cpaCyPrimeTest(
    cyInstHandle,
    (const CpaCyPrimeTestCbFunc)primeCallback, /* CB function */
    (void *)&complete, /* Callback tag */
    pPrimeTestOpData, /* Operation data */
    &testPassed); /* Return value: true if the number is probably a prime, false if it is not a prime */

Finally, statistics are queried and the service stopped.

Using the SM2 API

This example demonstrates the usage of the SM2 API.

The following steps are carried out:

  • The example contains synchronous and asynchronous API. For the latter one, you must define a proper callback function per the API prototype for different SM2 operations.

  • Instance, discovery, and start-up are made in a way similar to that defined for the symmetric examples above.

  • This sample involves sign/verify, encryption/decryption, key exchange, point multiplication/verify.

SM2 Digital Signature Generation and Verification

This operation is to sign a given message and output its digital signature (r,s) then verify (r,s).

The function sampleEcsm2SignPerformOp provisions parts of the example implementation, which does the following:

  • Allocate memory and populate data for input buffer which includes scalar multiplier k, digest of the message e and private key d.

  • Allocate memory for output buffer which includes signature r and s.

  • Call function cpaCyEcsm2Sign for sign operation.

status = OS_MALLOC(&pCpaEcsm2SignOpData, sizeof(CpaCyEcsm2SignOpData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2SignOpData->k.pData, sizeof(k));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2SignOpData->e.pData, sizeof(e));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2SignOpData->d.pData, sizeof(d));

status = PHYS_CONTIG_ALLOC(&pR->pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pS->pData, GFP_SM2_SIZE_IN_BYTE);

status = cpaCyEcsm2Sign(
    cyInstHandle,
    (const CpaCyEcsm2SignCbFunc)asymSignCallback, /* CB function*/
    pCallbackTag, /* Opaque user data */
    pCpaEcsm2SignOpData, /* Structure containing k, d and e */
    &signStatus, /* signStatus indicates if the result is valid */
    pR, /* Signature r (function output) */
    pS); /* Signature s (function output) */

The function sampleEcsm2VerifyPerformOp provisions parts of the example implementation, which does the following:

  • Allocate memory and populate data for the input buffer which includes digest of the message e, signature r and s, x coordinate of public key and y coordinate of public key.

  • Call the function cpaCyEcsm2Verify for signature verification operation.

status = OS_MALLOC(&pCpaEcsm2VerifyOpData, sizeof(CpaCyEcsm2VerifyOpData));

status = PHYS_CONTIG_ALLOC(&pCpaEcsm2VerifyOpData->e.pData, sizeof(e));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2VerifyOpData->r.pData, sizeof(r));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2VerifyOpData->s.pData, sizeof(s));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2VerifyOpData->xP.pData, sizeof(xP));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2VerifyOpData->yP.pData, sizeof(yPA));

status = cpaCyEcsm2Verify(
    cyInstHandle,
    (const CpaCyEcsm2VerifyCbFunc)asymVerifyCallback, /* CB function*/
    pCallbackTag, /* Opaque user data */
    pCpaEcsm2VerifyOpData, /* Verify request data*/
    &verifyStatus); /* Verify status */

SM2 Public Key Encryption

This operation is to encrypt a given message then decrypt the cipher and compare to the given message.

The function sampleEcsm2EncPerformOp provisions parts of the example implementation, which does the following:

  • Allocate memory and populate data for the input buffer which includes scalar multiplier k, x coordinate of public key xP and y coordinate of public key yP.

  • Allocate memory for the output buffer which includes x coordinate of [k]G x1, y coordinate of [k]G y1, x coordinate of [k]Pb x2 and y coordinate of [k]Pb y2.

  • Call the function cpaCyEcsm2Encrypt for encryption operation.

status = OS_MALLOC(&pCpaEcsm2EncOpData, sizeof(CpaCyEcsm2EncryptOpData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2EncOpData->k.pData, sizeof(k));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2EncOpData->xP.pData, sizeof(xP));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2EncOpData->yP.pData, sizeof(yP));

status = OS_MALLOC(&pCpaEcsm2EncOutputData, sizeof(CpaCyEcsm2EncryptOutputData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2EncOutputData->x1.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2EncOutputData->y1.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2EncOutputData->x2.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2EncOutputData->y2.pData, GFP_SM2_SIZE_IN_BYTE);

status = cpaCyEcsm2Encrypt(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)asymEncCallback, /* CB function*/
    pCallbackTag, /* Opaque user data */
    pCpaEcsm2EncOpData, /* Encryption request data */
    pCpaEcsm2EncOutputData); /* Encryption response data */

The function sampleEcsm2DecPerformOp provisions parts of the example implementation, which does the following:

  • Allocate memory and populate data for the input buffer which includes private key d, x coordinate of [k]G x1 and y coordinate of [k]G y1.

  • Allocate memory for the output buffer which includes x coordinate of [k]Pb x2 and y coordinate of [k]Pb y2.

  • Call the function cpaCyEcsm2Decrypt for decryption operation.

  • Call the function sm3 and hashCheck to check correctness of decryption.

status = OS_MALLOC(&pCpaEcsm2DecOpData, sizeof(CpaCyEcsm2DecryptOpData));

status = PHYS_CONTIG_ALLOC(&pCpaEcsm2DecOpData->d.pData, sizeof(d));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2DecOpData->x1.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2DecOpData->y1.pData, GFP_SM2_SIZE_IN_BYTE);

status = OS_MALLOC(&pCpaEcsm2DecOutputData, sizeof(CpaCyEcsm2DecryptOutputData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2DecOutputData->x2.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2DecOutputData->y2.pData, GFP_SM2_SIZE_IN_BYTE);

status = cpaCyEcsm2Decrypt(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)asymDecCallback, /* CB function*/
    pCallbackTag, /* Opaque user data */
    pCpaEcsm2DecOpData, /* Decryption request data */
    pCpaEcsm2DecOutputData); /* Decryption response data */

sm3(pDecOutputData, MESSAGE_LEN + 2 * GFP_SM2_SIZE_IN_BYTE, pHashBuffer);
hashCheck(pC3Buffer, pHashBuffer, &status);

SM2 Key Exchange

This operation is to exchange key between the A side and B side, and check if the shared keys are the same.

The function sampleEcsm2KeyExPerformOp provisions parts of the example implementation, which does the following:

  • Allocate the phase 1 input buffer which includes scalar multiplier r for the A side and B side separately.

  • Allocate the phase 1 output buffer which includes x coordinate of a point on the curve x and y coordinate of a point on the curve y for the A side and B side separately.

  • Call function cpaCyEcsm2KeyExPhase1 for the A side and B side separately.

  • Allocate the phase 2 input buffer which includes scalar multiplier r, private key d, x coordinate of a point on the curve from other side x1, x coordinate of a point on the curve from phase 1 x2, y coordinate of a point on the curve from phase 1 y2, x coordinate of public key from other side xP and y coordinate of public key from other side yP for the A side and B side separately.

  • Allocate the phase 2 output buffer which includes x coordinate of a point on the curve x and y coordinate of a point on the curve y for the A side and B side separately.

  • Call function cpaCyEcsm2KeyExPhase2 for the A side and B side separately.

status = OS_MALLOC(&pCpaEcsm2KeyExPhase1AOpData, sizeof(CpaCyEcsm2KeyExPhase1OpData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase1AOpData->r.pData, sizeof(rA));

status = OS_MALLOC(&pCpaEcsm2KeyExPhase1BOpData, sizeof(CpaCyEcsm2KeyExPhase1OpData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase1BOpData->r.pData, sizeof(rB));

status = OS_MALLOC(&pCpaEcsm2KeyExPhase1AOutputData, sizeof(CpaCyEcsm2KeyExOutputData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase1AOutputData->x.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase1AOutputData->y.pData, GFP_SM2_SIZE_IN_BYTE);

status = OS_MALLOC(&pCpaEcsm2KeyExPhase1BOutputData, sizeof(CpaCyEcsm2KeyExOutputData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase1BOutputData->x.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase1BOutputData->y.pData, GFP_SM2_SIZE_IN_BYTE);

status = cpaCyEcsm2KeyExPhase1(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)
    asymKeyExPhase1Callback, /* CB function*/
    pCallbackTag, /* Opaque user data */
    pCpaEcsm2KeyExPhase1AOpData, /* Key exchange p1 request data */
    pCpaEcsm2KeyExPhase1AOutputData); /* Key exchange p1 response data */

status = cpaCyEcsm2KeyExPhase1(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)
    asymKeyExPhase1Callback, /* CB function*/
    pCallbackTag, /* Opaque user data */
    pCpaEcsm2KeyExPhase1BOpData, /* Key exchange p1 request data */
    pCpaEcsm2KeyExPhase1BOutputData); /* Key exchange p1 request data */

status = OS_MALLOC(&pCpaEcsm2KeyExPhase2AOpData, sizeof(CpaCyEcsm2KeyExPhase2OpData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOpData->r.pData, sizeof(rA));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOpData->d.pData, sizeof(dA));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOpData->x1.pData, pCpaEcsm2KeyExPhase2AOpData->x1.dataLenInBytes);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOpData->x2.pData, pCpaEcsm2KeyExPhase1BOutputData->x.dataLenInBytes);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOpData->y2.pData, pCpaEcsm2KeyExPhase1BOutputData->y.dataLenInBytes);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOpData->xP.pData, sizeof(xPB));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOpData->yP.pData, sizeof(yPB));

status = OS_MALLOC(&pCpaEcsm2KeyExPhase2BOpData, sizeof(CpaCyEcsm2KeyExPhase2OpData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOpData->r.pData, sizeof(rB));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOpData->d.pData, sizeof(dB));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOpData->x1.pData, pCpaEcsm2KeyExPhase2BOpData->x1.dataLenInBytes);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOpData->x2.pData, pCpaEcsm2KeyExPhase1AOutputData->x.dataLenInBytes);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOpData->y2.pData, pCpaEcsm2KeyExPhase1AOutputData->y.dataLenInBytes);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOpData->xP.pData, sizeof(xPA));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOpData->yP.pData, sizeof(yPA));

status = OS_MALLOC(&pCpaEcsm2KeyExPhase2AOutputData, sizeof(CpaCyEcsm2KeyExOutputData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOutputData->x.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2AOutputData->y.pData, GFP_SM2_SIZE_IN_BYTE);

status = OS_MALLOC(&pCpaEcsm2KeyExPhase2BOutputData, sizeof(CpaCyEcsm2KeyExOutputData));
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOutputData->x.pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pCpaEcsm2KeyExPhase2BOutputData->y.pData, GFP_SM2_SIZE_IN_BYTE);

status = cpaCyEcsm2KeyExPhase2(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)asymKeyExPhase2Callback,
    pCallbackTag, /* Opaque user data; */
    pCpaEcsm2KeyExPhase2AOpData, /* Key exchange p2 request data, containing r,d,x1,x2,y2,xp,yp */
    pCpaEcsm2KeyExPhase2AOutputData); /* Key exchange p2 response data */

status = cpaCyEcsm2KeyExPhase2(
    cyInstHandle,
    (const CpaCyGenFlatBufCbFunc)asymKeyExPhase2Callback,
    pCallbackTag, /* Opaque user data */
    pCpaEcsm2KeyExPhase2BOpData, /* Key exchange p2 request data containing r,d,x1,y1,x2,y2,xp,yp */
    pCpaEcsm2KeyExPhase2BOutputData); /* Key exchange p2 response data */

SM2 Elliptic Curve Point

This operation is to calculate a point on the curve according to a given random number and verify if the point (x,y) is on the curve or not.

The function sampleEcsm2PointMultiply provisions parts of the example implementation, which does the following:

  • Allocate memory and populate date for input buffer which includes scalar multiplier k, x coordinate of a point on the curve x and y coordinate of a point on the curve y.

  • Allocate memory for output buffer which includes x coordinate of the resulting point multiplication pXk and y coordinate of the resulting point multiplication pYk.

  • Call function cpaCyEcsm2PointMultiply for point multiply operation.

status = OS_MALLOC(&opData, sizeof(CpaCyEcsm2PointMultiplyOpData));
status = PHYS_CONTIG_ALLOC(&opData->k.pData, sizeof(k));
status = PHYS_CONTIG_ALLOC(&opData->x.pData, sizeof(xP));
status = PHYS_CONTIG_ALLOC(&opData->y.pData, sizeof(yP));

status = OS_MALLOC(&pXk, sizeof(CpaFlatBuffer));
status = OS_MALLOC(&pYk, sizeof(CpaFlatBuffer));
status = PHYS_CONTIG_ALLOC(&pXk->pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pYk->pData, GFP_SM2_SIZE_IN_BYTE);

status = cpaCyEcsm2PointMultiply(
    cyInstHandle,
    (const CpaCyEcPointMultiplyCbFunc)
    asymPointMultCallback, /* CB function*/
    pCallbackTag, /* Opaque user data; */
    opData, /* Point multiplication request data */
    &multiplyStatus,
    pXk, /* Point multiplication response data */
    pYk); /* Point multiplication response data */

The function sampleEcsm2GeneratorMultiply provisions parts of the example implementation, which does the following:

  • Allocate memory and populate date for the input buffer which includes the scalar multiplier k.

  • Allocate memory for the output buffer which includes x coordinate of the resulting point multiplication pXk and y coordinate of the resulting point multiplication pYk.

  • Call the function cpaCyEcsm2GeneratorMultiply for generator multiply operation.

status = OS_MALLOC(&opData, sizeof(CpaCyEcsm2GeneratorMultiplyOpData));
status = PHYS_CONTIG_ALLOC(&opData->k.pData, sizeof(k));

status = OS_MALLOC(&pXk, sizeof(CpaFlatBuffer));
status = OS_MALLOC(&pYk, sizeof(CpaFlatBuffer));
status = PHYS_CONTIG_ALLOC(&pXk->pData, GFP_SM2_SIZE_IN_BYTE);
status = PHYS_CONTIG_ALLOC(&pYk->pData, GFP_SM2_SIZE_IN_BYTE);

status = cpaCyEcsm2GeneratorMultiply(
    cyInstHandle,
    (const CpaCyEcPointMultiplyCbFunc) asymGeneratorMultCallback, /* CB function*/
    pCallbackTag, /* Opaque user data; */
    opData, /* Generator multiplication request data */
    &multiplyStatus,
    pXk, /* Generator multiplication response data */
    pYk); /* Generator multiplication response data */

The function sampleEcsm2PointVerify provisions parts of the example implementation, which does the following:

  • Allocate memory and populate date for the input buffer which includes x coordinate of a point on the curve x and y coordinate of a point on the curve y.

  • Call the function cpaCyEcsm2PointVerify for EC point verification.

status = OS_MALLOC(&opData, sizeof(CpaCyEcsm2PointVerifyOpData));
status = PHYS_CONTIG_ALLOC(&opData->x.pData, sizeof(x));
status = PHYS_CONTIG_ALLOC(&opData->y.pData, sizeof(y));

status = cpaCyEcsm2PointVerify(
    cyInstHandle,
    (const CpaCyEcPointVerifyCbFunc) asymPointVerifyCallback, /* CB function*/
    pCallbackTag, /* Opaque user data */
    opData, /* Point verify request data */
    &verifyStatus);

Finally, statistics are queried and the service stopped.