文章主要讲述了Intel® QAT 加密API接口的说明,以及多种应用场景下的使用方法。
Intel® QAT加速卡API接口可以分为以下几类:
加密操作使用了一些基本API接口,这些API接口定义了基本的数据类型供QAT加速服务使用。
对称类API是唯一使用到会话概念的API。会话的定义见下面章节。
加密对称API支持优先级。 优先级可以永久指定指定。对称API支持两种优先级:高优先级和普通优先级。
实现可以使用严格的优先级顺序,也可以使用基于加权轮询的优先级方案。
这部分主要讲述了如何使用对称加密接口执行各种各样的加密和哈希操作。
对称加密操作都遵循以下几个基本的操作步骤:
下面分别介绍三个涉及到的基本概念。
在对称API的情况下,会话作为一个句柄,用于描述要应用于多个缓冲区(buffers)的加密参数。这些buffers可能是用于加密单个文件,也可能是通过IPSec隧道的所有数据包或者与之相关的安全联盟。会话句柄包含以下几类数据:
会话进行的操作,例如加密操作,哈希操作,或者两个操作;这种情况下需要设置算法执行顺序;
所有的加密材料,包括加密算法、模式、密钥、密钥长度、方向(加密、解密)等;
哈希操作材料,包括散列算法,模式(普通,嵌套或已认证)和摘要结果长度(允许截断);
In-Place操作意味着目标缓冲区与源缓冲区相同。 Out-of-Place操作意味着目标缓冲区与源缓冲区不同。
官方提供的文档中,大多数示例都对完整的数据包进行操作。 此外,该API还支持在部分模式下运行; 例如,状态(例如密码状态)需要从一个分组/记录转发到下一个分组/记录。
下面通过一个例子来说明对称API的用法。
quickassist\lookaside\access_layer\src\sample_code\functional\sym\cipher_sample
如果要使用异步模式API,就必须提供一个回调函数。 异步操作完成后,会自动执行回调函数。 回调函数运行的上下文取决于实现。 例如,可以在Linux中断处理程序的下半部分的上下文中或在用户创建的轮询线程的上下文中调用它。 调用此函数的上下文对在回调函数中可以执行的处理施加了限制。例如,它声明该函数不应休眠(因为它可能在不允许休眠的上下文中被调用,例如Linux中断下半部)。
此功能可以执行适合应用程序的任何处理。 例如,它可以释放内存,继续处理解密的数据包等。在此示例中,该函数仅设置complete变量以表明它已被调用,如下所示。
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);
}
}
这是加密操作的主要入口点。它演示了一些重要的API接口的调用顺序,包括执行一个、多个加密操作,拆除会话等。下面是几个重要的步骤:
获取加密实例(Instance)的接口在此示例中是通过sampleCyGetInstance
接口来实现的。它是一个比较简单的API接口,查询所有的Instance,但是只返回第一个Instance。它的实现如下:
#ifdef DO_CRYPTO
void
sampleCyGetInstance(CpaInstanceHandle* pCyInstHandle)
{
CpaInstanceHandle cyInstHandles[MAX_INSTANCES];/*#define MAX_INSTANCES 1*/
Cpa16U numInstances = 0;
CpaStatus status = CPA_STATUS_SUCCESS;
*pCyInstHandle = NULL;
status = cpaCyGetNumInstances(&numInstances);/*获取instance的个数*/
if ((status == CPA_STATUS_SUCCESS) && (numInstances > 0))
{
/*获取MAX_INSTANCES个示例,并返回第一个示例的句柄*/
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
设置Instance的地址转换函数
CpaStatus
cipherSample(void)
{
... ...
if(CPA_STATUS_SUCCESS == status)
{
/*
* Set the address translation function for the instance
*/
//
status = cpaCySetAddressTranslation(cyInstHandle, sampleVirtToPhys);
//
}
... ...
}
启动加密服务Instance。
CpaStatus
cipherSample(void)
{
... ...
status = cpaCyStartInstance(cyInstHandle);
... ...
}
接下来便是创建并初始化加密会话。首先需要填充会话初始化操作数据结构的相关字段。需要注意的是:存储会话数据的空间大小与具体实现有关,这就要求我们需要先通过API接口获取到需要的会话大小,然后在分配空间。这里有两个接口可以使用:
这个函数始终返回最大会话大小。
在满足特定的会话建立条件情况下,使用此函数可以返回比较小的内存空间大小。
CpaStatus
cipherSample(void)
{
... ...
{
sessionSetupData.sessionPriority = CPA_CY_PRIORITY_NORMAL;
sessionSetupData.symOperation = CPA_CY_SYM_OP_CIPHER;
sessionSetupData.cipherSetupData.cipherAlgorithm =
CPA_CY_SYM_CIPHER_3DES_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*/
}
... ...
}
在完成上述操作之后,便可以通过调用cipherPerformOp()
来完成加密操作,下面继续说明cipherPerformOp
中涉及的重要流程:
不同API实现需要不同大小的内存空间来存储与缓冲区列表关联的元数据。 查询相应的API获取到需要分配的空间的大小,然后为缓冲区元数据,缓冲区列表以及缓冲区本身分配空间。 此外,还必须为初始化向量分配内存。
static CpaStatus
cipherPerformOp(CpaInstanceHandle cyInstHandle, CpaCySymSessionCtx sessionCtx)
{
... ...
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));
}
... ...
}
源缓冲区和初始化向量申请的内存中需要填充所需的数据。构造操作数据:填充执行加密算法是所需的操作数据的结构。
static CpaStatus
cipherPerformOp(CpaInstanceHandle cyInstHandle, CpaCySymSessionCtx sessionCtx)
{
... ...
if (CPA_STATUS_SUCCESS == status)
{
/*
* Populate the structure containing the operational data needed
* to run the algorithm:
* - packet type information (the algorithm can operate on a full
* packet, perform a partial operation and maintain the state or
* complete the last part of a multi-part operation)
* - the initialization vector and its length
* - the offset in the source buffer
* - the length of the source message
*/
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);
}
... ...
}
首先初始化complete
信号量(通过此信号量可以知道执行的操作是否完成),然后执行加密操作。
static CpaStatus
cipherPerformOp(CpaInstanceHandle cyInstHandle, CpaCySymSessionCtx sessionCtx)
{
... ...
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);
... ...
}
通过信号量(PV操作)来判断加速卡是否完成所进行的操作()。
if (!COMPLETION_WAIT(&complete, TIMEOUT_MS)) {
PRINT_ERR("timeout or interruption in cpaCySymPerformOp\n");
status = CPA_STATUS_FAIL;
}
在实际的使用过程中,一个会话上可能同时存在多个待处理的数据报文。因此在删除会话之前,通过调用次此函数等待会话上所有的操作的完成,之后再进行会话删除操作。
symSessionWaitForInflightReq(sessionCtx);
sessionStatus = cpaCySymRemoveSession(cyInstHandle, sessionCtx);
停止进行轮询,停止instance。
sampleCyStopPolling();
PRINT_DBG("cpaCyStopInstance\n");
cpaCyStopInstance(cyInstHandle);
该示例主要为了演示使用对称API进行哈希操作的用法。示例中使用MD5算法进行的哈希操作。进行哈希操作的流程与加密操作基本一致,这里再做进一步说明:
qat1.5.l.1.13.0-19\quickassist\lookaside\access_layer\src\sample_code\functional\sym\hash_sample
CpaStatus
hashSample(void)
{
CpaStatus status = CPA_STATUS_SUCCESS;
Cpa32U sessionCtxSize = 0;
CpaInstanceHandle cyInstHandle = NULL;
CpaCySymSessionCtx sessionCtx = NULL;
CpaCySymSessionSetupData sessionSetupData = {0};
CpaCySymStats64 symStats = {0};
/*
* In this simplified version of instance discovery, we discover
* exactly one instance of a crypto service.
*/
sampleCyGetInstance(&cyInstHandle);
if (cyInstHandle == NULL)
{
return CPA_STATUS_FAIL;
}
/* Start Cryptographic component */
PRINT_DBG("cpaCyStartInstance\n");
status = cpaCyStartInstance(cyInstHandle);
if(CPA_STATUS_SUCCESS == status)
{
/*
* Set the address translation function for the instance
*/
status = cpaCySetAddressTranslation(cyInstHandle, sampleVirtToPhys);
}
if (CPA_STATUS_SUCCESS == status)
{
/*
* If the instance is polled start the polling thread. Note that
* how the polling is done is implementation-dependant.
*/
sampleCyStartPolling(cyInstHandle);
/*
* We now populate the fields of the session operational data and create
* the session. Note that the size required to store a session is
* implementation-dependent, so we query the API first to determine how
* much memory to allocate, and then allocate that memory.
*/
//
/* 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_MD5;
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;
//
/* 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);
}
if (CPA_STATUS_SUCCESS == status)
{
/* Initialize the Hash session */
PRINT_DBG("cpaCySymInitSession\n");
status = cpaCySymInitSession(cyInstHandle,
symCallback, &sessionSetupData, sessionCtx);
}
if (CPA_STATUS_SUCCESS == status)
{
CpaStatus sessionStatus = CPA_STATUS_SUCCESS;
/* Perform Hash operation */
status = hashPerformOp(cyInstHandle, sessionCtx);
/* Remove the session - session init has already succeeded */
PRINT_DBG("cpaCySymRemoveSession\n");
sessionStatus = cpaCySymRemoveSession(
cyInstHandle, sessionCtx);
/* maintain status of remove session only when status of all operations
* before it are successful. */
if (CPA_STATUS_SUCCESS == status)
{
status = sessionStatus;
}
}
if (CPA_STATUS_SUCCESS == status)
{
/* Query symmetric statistics */
status = cpaCySymQueryStats64(cyInstHandle, &symStats);
if (CPA_STATUS_SUCCESS != status)
{
PRINT_ERR("cpaCySymQueryStats failed, status = %d\n", status);
}
else
{
PRINT_DBG("Number of symmetric operation completed: %llu\n",
(unsigned long long)symStats.numSymOpCompleted);
}
}
/* Clean up */
/* Free session Context */
PHYS_CONTIG_FREE(sessionCtx);
/* Stop the polling thread */
sampleCyStopPolling();
PRINT_DBG("cpaCyStopInstance\n");
cpaCyStopInstance(cyInstHandle);
if (CPA_STATUS_SUCCESS == status)
{
PRINT_DBG("Sample code ran successfully\n");
}
else
{
PRINT_DBG("Sample code failed with status of %d\n", status);
}
return status;
}
static CpaStatus
hashPerformOp(CpaInstanceHandle cyInstHandle, CpaCySymSessionCtx sessionCtx)
{
CpaStatus status = CPA_STATUS_SUCCESS;
Cpa8U *pBufferMeta = NULL;
Cpa32U bufferMetaSize = 0;
CpaBufferList *pBufferList = NULL;
CpaFlatBuffer *pFlatBuffer = NULL;
CpaCySymOpData *pOpData = NULL;
Cpa32U bufferSize = sizeof(vectorData) + DIGEST_LENGTH;
Cpa32U numBuffers = 1; /* only using 1 buffer in this case */
/* allocate memory for bufferlist and array of flat buffers in a contiguous
* area and carve it up to reduce number of memory allocations required. */
Cpa32U bufferListMemSize = sizeof(CpaBufferList) + (numBuffers * sizeof(CpaFlatBuffer));
Cpa8U *pSrcBuffer = NULL;
Cpa8U *pDigestBuffer = NULL;
/* The following variables are allocated on the stack because we block
* until the callback comes back. If a non-blocking approach was to be
* used then these variables should be dynamically allocated */
struct COMPLETION_STRUCT complete;
/* get meta information size */
PRINT_DBG("cpaCyBufferListGetMetaSize\n");
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)
{
/* copy vector into buffer */
memcpy(pSrcBuffer, vectorData, sizeof(vectorData));
pDigestBuffer = pSrcBuffer + sizeof(vectorData);/*填充摘要部分的起始地址*/
/* increment by sizeof(CpaBufferList) to get at the
* array of flatbuffers */
pFlatBuffer = (CpaFlatBuffer *) (pBufferList + 1);
pBufferList->pBuffers = pFlatBuffer;
pBufferList->numBuffers = 1;
pBufferList->pPrivateMetaData = pBufferMeta;
pFlatBuffer->dataLenInBytes = bufferSize;
pFlatBuffer->pData = pSrcBuffer;
status = OS_MALLOC(&pOpData, sizeof(CpaCySymOpData));
}
if (CPA_STATUS_SUCCESS == status)
{
//
pOpData->sessionCtx = sessionCtx;
pOpData->packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
pOpData->hashStartSrcOffsetInBytes = 0;
pOpData->messageLenToHashInBytes = sizeof(vectorData);
pOpData->pDigestResult = pDigestBuffer;
//
}
if (CPA_STATUS_SUCCESS == status)
{
/** initialisation for callback; the "complete" variable is used by the
* callback function to indicate it has been called*/
COMPLETION_INIT((&complete));
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);
}
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;
}
}
if (CPA_STATUS_SUCCESS == status)
{
if (0 == memcmp(digest, pOpData->pDigestResult, DIGEST_LENGTH))
{
PRINT_DBG("Digest matches expected output\n");
}
else
{
PRINT_DBG("Digest does not match expected output\n");
status = CPA_STATUS_FAIL;
}
}
}
/* At this stage, the callback function should have returned,
* so it is safe to free the memory */
PHYS_CONTIG_FREE(pSrcBuffer);
OS_FREE(pBufferList);
PHYS_CONTIG_FREE(pBufferMeta);
OS_FREE(pOpData);
COMPLETION_DESTROY(&complete);
return status;
}
对一个文件进行哈希操作的处理流程与上述的哈希操作流程基本一致,只是在计算数据时有所不同:为了执行对文件进行哈希的操作,将数据从文件读取到源缓冲区,并在packetType设置为CPA_CY_SYM_PACKET_TYPE_PARTIAL的情况下重复调用对称API。 到达文件末尾时,将使用packetType设置为CPA_CY_SYM_PACKET_TYPE_PARTIAL_LAST, 然后调用API计算最后的一包数据。 哈希值仅在最后一次调用时产生API。
if (CPA_STATUS_SUCCESS == status)
{
/** initialisation for callback; the "complete" variable is used by the
* callback function to indicate it has been called*/
COMPLETION_INIT((&complete));
//
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;
}
}
}