490 lines
14 KiB
C
490 lines
14 KiB
C
/* FIPS Known answer tests for QCEDEV / FIPS-non-FIPS separation .
|
|
*
|
|
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/mman.h>
|
|
#include <linux/types.h>
|
|
#include <linux/export.h>
|
|
#include <linux/qcedev.h>
|
|
#include "qcedevi.h"
|
|
#include "qcedev_fips.h"
|
|
|
|
/*
|
|
* Initiate the session handle (like open /dev/qce)
|
|
*/
|
|
static int _fips_initiate_qcedev_handle(struct qcedev_control *podev,
|
|
struct qcedev_async_req *qcedev_areq)
|
|
{
|
|
struct qcedev_handle *handle;
|
|
|
|
handle = kzalloc(sizeof(struct qcedev_handle), GFP_KERNEL);
|
|
if (handle == NULL) {
|
|
pr_err("Failed to allocate memory %ld\n", PTR_ERR(handle));
|
|
return -ENOMEM;
|
|
}
|
|
|
|
handle->cntl = podev;
|
|
qcedev_areq->handle = handle;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*Initiate QCEDEV request for sha/hmac
|
|
*/
|
|
static
|
|
int _fips_initiate_qcedev_async_req_sha(struct qcedev_async_req *qcedev_areq,
|
|
struct scatterlist *fips_sg,
|
|
int tv_index)
|
|
{
|
|
qcedev_areq->sha_op_req.alg =
|
|
fips_test_vector_sha_hmac[tv_index].hash_alg;
|
|
|
|
/* If HMAC setup key else make key length zero */
|
|
if ((qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA1_HMAC) ||
|
|
(qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA256_HMAC) ||
|
|
(qcedev_areq->sha_op_req.alg == QCEDEV_ALG_AES_CMAC)) {
|
|
qcedev_areq->sha_op_req.authkey =
|
|
&fips_test_vector_sha_hmac[tv_index].key[0];
|
|
qcedev_areq->sha_op_req.authklen =
|
|
fips_test_vector_sha_hmac[tv_index].klen;
|
|
} else
|
|
qcedev_areq->sha_op_req.authklen = 0;
|
|
|
|
/* Setup input and digest */
|
|
qcedev_areq->sha_op_req.data[0].vaddr =
|
|
&fips_test_vector_sha_hmac[tv_index].input[0];
|
|
qcedev_areq->sha_op_req.data[0].len =
|
|
fips_test_vector_sha_hmac[tv_index].ilen;
|
|
qcedev_areq->sha_op_req.data_len =
|
|
fips_test_vector_sha_hmac[tv_index].ilen;
|
|
|
|
/* Setup sha context and other parameters */
|
|
qcedev_areq->sha_op_req.entries = 1;
|
|
qcedev_areq->op_type = QCEDEV_CRYPTO_OPER_SHA;
|
|
memset(&qcedev_areq->handle->sha_ctxt, 0,
|
|
sizeof(struct qcedev_sha_ctxt));
|
|
qcedev_areq->handle->sha_ctxt.first_blk = 1;
|
|
|
|
/* Initialize digest and digest length */
|
|
memset(&qcedev_areq->sha_op_req.digest[0], 0, QCEDEV_MAX_SHA_DIGEST);
|
|
qcedev_areq->sha_op_req.diglen =
|
|
fips_test_vector_sha_hmac[tv_index].diglen;
|
|
switch (qcedev_areq->sha_op_req.alg) {
|
|
case QCEDEV_ALG_SHA1:
|
|
case QCEDEV_ALG_SHA1_HMAC:
|
|
memcpy(&qcedev_areq->handle->sha_ctxt.digest[0],
|
|
&_std_init_vector_sha1_uint8[0],
|
|
SHA1_DIGEST_SIZE);
|
|
break;
|
|
case QCEDEV_ALG_SHA256:
|
|
case QCEDEV_ALG_SHA256_HMAC:
|
|
memcpy(&qcedev_areq->handle->sha_ctxt.digest[0],
|
|
&_std_init_vector_sha256_uint8[0],
|
|
SHA256_DIGEST_SIZE);
|
|
break;
|
|
case QCEDEV_ALG_AES_CMAC:
|
|
qcedev_areq->handle->sha_ctxt.diglen =
|
|
fips_test_vector_sha_hmac[tv_index].diglen;
|
|
break;
|
|
default:
|
|
pr_err(" _fips_initiate_qcedev_async_req_sha : Invalid algo");
|
|
return -EINVAL;
|
|
}
|
|
|
|
qcedev_areq->handle->sha_ctxt.init_done = true;
|
|
qcedev_areq->handle->sha_ctxt.trailing_buf_len =
|
|
qcedev_areq->sha_op_req.data_len;
|
|
memcpy(&qcedev_areq->handle->sha_ctxt.trailing_buf[0],
|
|
fips_test_vector_sha_hmac[tv_index].input,
|
|
fips_test_vector_sha_hmac[tv_index].ilen);
|
|
qcedev_areq->handle->sha_ctxt.last_blk = 1;
|
|
qcedev_areq->sha_req.sreq.nbytes = qcedev_areq->sha_op_req.data_len;
|
|
qcedev_areq->sha_req.cookie = qcedev_areq->handle;
|
|
qcedev_areq->sha_req.sreq.src = fips_sg;
|
|
sg_set_buf(qcedev_areq->sha_req.sreq.src,
|
|
&qcedev_areq->handle->sha_ctxt.trailing_buf[0],
|
|
qcedev_areq->sha_op_req.data_len);
|
|
sg_mark_end(qcedev_areq->sha_req.sreq.src);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Clean up of sha context after request completion
|
|
*/
|
|
static void _fips_clear_qcedev_handle(struct qcedev_sha_ctxt *sha_ctxt)
|
|
{
|
|
sha_ctxt->first_blk = 0;
|
|
sha_ctxt->last_blk = 0;
|
|
sha_ctxt->auth_data[0] = 0;
|
|
sha_ctxt->auth_data[1] = 0;
|
|
sha_ctxt->trailing_buf_len = 0;
|
|
sha_ctxt->init_done = false;
|
|
memset(&sha_ctxt->trailing_buf[0], 0, 64);
|
|
}
|
|
|
|
/*
|
|
* Self test for SHA / HMAC
|
|
*/
|
|
int _fips_qcedev_sha_selftest(struct qcedev_control *podev)
|
|
{
|
|
int ret = 0, tv_index, num_tv;
|
|
struct qce_sha_req sreq;
|
|
struct qcedev_async_req qcedev_areq;
|
|
struct scatterlist fips_sg;
|
|
|
|
/* Initiate handle */
|
|
if (_fips_initiate_qcedev_handle(podev, &qcedev_areq))
|
|
return -ENOMEM;
|
|
|
|
num_tv = (sizeof(fips_test_vector_sha_hmac))/
|
|
(sizeof(struct _fips_test_vector_sha_hmac));
|
|
|
|
/* Tests one by one */
|
|
for (tv_index = 0; tv_index < num_tv; tv_index++) {
|
|
init_completion(&qcedev_areq.complete);
|
|
|
|
/* Initiate the qcedev request */
|
|
if (_fips_initiate_qcedev_async_req_sha(&qcedev_areq,
|
|
&fips_sg, tv_index))
|
|
return -EINVAL;
|
|
|
|
podev->active_command = &qcedev_areq;
|
|
|
|
/* Initiate qce hash request */
|
|
sreq.qce_cb = qcedev_sha_req_cb;
|
|
if (qcedev_areq.sha_op_req.alg != QCEDEV_ALG_AES_CMAC) {
|
|
sreq.digest = &qcedev_areq.handle->sha_ctxt.digest[0];
|
|
sreq.first_blk = qcedev_areq.handle->sha_ctxt.first_blk;
|
|
sreq.last_blk = qcedev_areq.handle->sha_ctxt.last_blk;
|
|
sreq.auth_data[0] =
|
|
qcedev_areq.handle->sha_ctxt.auth_data[0];
|
|
sreq.auth_data[1] =
|
|
qcedev_areq.handle->sha_ctxt.auth_data[1];
|
|
sreq.auth_data[2] =
|
|
qcedev_areq.handle->sha_ctxt.auth_data[2];
|
|
sreq.auth_data[3] =
|
|
qcedev_areq.handle->sha_ctxt.auth_data[3];
|
|
}
|
|
|
|
sreq.size = qcedev_areq.sha_req.sreq.nbytes;
|
|
sreq.src = qcedev_areq.sha_req.sreq.src;
|
|
sreq.areq = (void *)&qcedev_areq.sha_req;
|
|
sreq.flags = 0;
|
|
switch (qcedev_areq.sha_op_req.alg) {
|
|
case QCEDEV_ALG_SHA1:
|
|
sreq.alg = QCE_HASH_SHA1;
|
|
break;
|
|
case QCEDEV_ALG_SHA256:
|
|
sreq.alg = QCE_HASH_SHA256;
|
|
break;
|
|
case QCEDEV_ALG_SHA1_HMAC:
|
|
sreq.alg = QCE_HASH_SHA1_HMAC;
|
|
sreq.authkey = &qcedev_areq.sha_op_req.authkey[0];
|
|
sreq.authklen = qcedev_areq.sha_op_req.authklen;
|
|
break;
|
|
case QCEDEV_ALG_SHA256_HMAC:
|
|
sreq.alg = QCE_HASH_SHA256_HMAC;
|
|
sreq.authkey =
|
|
&qcedev_areq.sha_op_req.authkey[0];
|
|
sreq.authklen =
|
|
qcedev_areq.sha_op_req.authklen;
|
|
break;
|
|
case QCEDEV_ALG_AES_CMAC:
|
|
sreq.alg = QCE_HASH_AES_CMAC;
|
|
sreq.authkey =
|
|
&qcedev_areq.sha_op_req.authkey[0];
|
|
sreq.authklen =
|
|
qcedev_areq.sha_op_req.authklen;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto handle_free;
|
|
}
|
|
|
|
/*qce call */
|
|
ret = qce_process_sha_req(podev->qce, &sreq);
|
|
if (ret == 0)
|
|
wait_for_completion(&qcedev_areq.complete);
|
|
else
|
|
goto handle_free;
|
|
|
|
/* Known answer test */
|
|
if (memcmp(&qcedev_areq.handle->sha_ctxt.digest[0],
|
|
fips_test_vector_sha_hmac[tv_index].digest,
|
|
fips_test_vector_sha_hmac[tv_index].diglen)) {
|
|
ret = -1;
|
|
goto handle_free;
|
|
}
|
|
_fips_clear_qcedev_handle(&qcedev_areq.handle->sha_ctxt);
|
|
}
|
|
|
|
handle_free:
|
|
kzfree(qcedev_areq.handle);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Initiate QCEDEV request for cipher (Encryption/ Decryption requests)
|
|
*/
|
|
static
|
|
void _fips_initiate_qcedev_async_req_cipher(
|
|
struct qcedev_async_req *qcedev_areq,
|
|
enum qcedev_oper_enum qcedev_oper,
|
|
struct scatterlist *fips_sg,
|
|
uint8_t *k_align_src,
|
|
int tv_index)
|
|
{
|
|
uint8_t *k_align_dst = k_align_src;
|
|
|
|
/* Setup Key */
|
|
memset(qcedev_areq->cipher_op_req.enckey, 0,
|
|
fips_test_vector_cipher[tv_index].klen);
|
|
memcpy(qcedev_areq->cipher_op_req.enckey,
|
|
fips_test_vector_cipher[tv_index].key,
|
|
fips_test_vector_cipher[tv_index].klen);
|
|
qcedev_areq->cipher_op_req.encklen =
|
|
fips_test_vector_cipher[tv_index].klen;
|
|
|
|
/* Setup IV */
|
|
memset(qcedev_areq->cipher_op_req.iv, 0,
|
|
fips_test_vector_cipher[tv_index].ivlen);
|
|
memcpy(qcedev_areq->cipher_op_req.iv,
|
|
fips_test_vector_cipher[tv_index].iv,
|
|
fips_test_vector_cipher[tv_index].ivlen);
|
|
qcedev_areq->cipher_op_req.ivlen =
|
|
fips_test_vector_cipher[tv_index].ivlen;
|
|
|
|
/* Setup other parameters */
|
|
qcedev_areq->cipher_op_req.byteoffset = 0;
|
|
qcedev_areq->cipher_op_req.alg =
|
|
fips_test_vector_cipher[tv_index].enc_alg;
|
|
qcedev_areq->cipher_op_req.mode =
|
|
fips_test_vector_cipher[tv_index].mode;
|
|
qcedev_areq->cipher_op_req.use_pmem = 0;
|
|
qcedev_areq->cipher_op_req.in_place_op = 1;
|
|
qcedev_areq->cipher_op_req.entries = 1;
|
|
qcedev_areq->cipher_op_req.op = qcedev_oper;
|
|
qcedev_areq->op_type = QCEDEV_CRYPTO_OPER_CIPHER;
|
|
|
|
/* Setup Input and output buffers */
|
|
if (qcedev_oper == QCEDEV_OPER_ENC) {
|
|
qcedev_areq->cipher_op_req.data_len =
|
|
fips_test_vector_cipher[tv_index].pln_txt_len;
|
|
qcedev_areq->cipher_op_req.vbuf.src[0].len =
|
|
fips_test_vector_cipher[tv_index].pln_txt_len;
|
|
} else {
|
|
qcedev_areq->cipher_op_req.data_len =
|
|
fips_test_vector_cipher[tv_index].enc_txt_len;
|
|
qcedev_areq->cipher_op_req.vbuf.src[0].len =
|
|
fips_test_vector_cipher[tv_index].enc_txt_len;
|
|
}
|
|
|
|
qcedev_areq->cipher_op_req.vbuf.src[0].vaddr =
|
|
&k_align_src[0];
|
|
qcedev_areq->cipher_op_req.vbuf.dst[0].vaddr =
|
|
&k_align_dst[0];
|
|
qcedev_areq->cipher_op_req.vbuf.dst[0].len =
|
|
fips_test_vector_cipher[tv_index].enc_txt_len;
|
|
|
|
qcedev_areq->cipher_req.creq.src = fips_sg;
|
|
qcedev_areq->cipher_req.creq.dst = fips_sg;
|
|
sg_set_buf(qcedev_areq->cipher_req.creq.src,
|
|
k_align_src,
|
|
qcedev_areq->cipher_op_req.data_len);
|
|
sg_mark_end(qcedev_areq->cipher_req.creq.src);
|
|
|
|
qcedev_areq->cipher_req.creq.nbytes =
|
|
qcedev_areq->cipher_op_req.data_len;
|
|
qcedev_areq->cipher_req.creq.info =
|
|
qcedev_areq->cipher_op_req.iv;
|
|
qcedev_areq->cipher_req.cookie = qcedev_areq->handle;
|
|
}
|
|
|
|
/*
|
|
* Initiate QCE request for cipher (Encryption/ Decryption requests)
|
|
*/
|
|
static int _fips_initiate_qce_req_cipher(struct qcedev_async_req *qcedev_areq,
|
|
struct qce_req *creq,
|
|
enum qce_cipher_dir_enum cipher_dir)
|
|
{
|
|
creq->dir = cipher_dir;
|
|
creq->iv = &qcedev_areq->cipher_op_req.iv[0];
|
|
creq->ivsize = qcedev_areq->cipher_op_req.ivlen;
|
|
creq->enckey = &qcedev_areq->cipher_op_req.enckey[0];
|
|
creq->encklen = qcedev_areq->cipher_op_req.encklen;
|
|
creq->cryptlen = qcedev_areq->cipher_op_req.data_len;
|
|
creq->op = QCE_REQ_ABLK_CIPHER;
|
|
creq->qce_cb = qcedev_cipher_req_cb;
|
|
creq->areq = (void *)&qcedev_areq->cipher_req;
|
|
creq->flags = 0;
|
|
switch (qcedev_areq->cipher_op_req.alg) {
|
|
case QCEDEV_ALG_3DES:
|
|
creq->alg = CIPHER_ALG_3DES;
|
|
break;
|
|
case QCEDEV_ALG_AES:
|
|
creq->alg = CIPHER_ALG_AES;
|
|
break;
|
|
default:
|
|
pr_err(" _fips_initiate_qce_req_cipher : Invalid algo");
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (qcedev_areq->cipher_op_req.mode) {
|
|
case QCEDEV_AES_MODE_CBC:
|
|
case QCEDEV_DES_MODE_CBC:
|
|
creq->mode = QCE_MODE_CBC;
|
|
break;
|
|
case QCEDEV_AES_MODE_ECB:
|
|
case QCEDEV_DES_MODE_ECB:
|
|
creq->mode = QCE_MODE_ECB;
|
|
break;
|
|
case QCEDEV_AES_MODE_CTR:
|
|
creq->mode = QCE_MODE_CTR;
|
|
break;
|
|
case QCEDEV_AES_MODE_XTS:
|
|
creq->mode = QCE_MODE_XTS;
|
|
break;
|
|
case QCEDEV_AES_MODE_CCM:
|
|
creq->mode = QCE_MODE_CCM;
|
|
break;
|
|
default:
|
|
pr_err(" _fips_initiate_qce_req_cipher : Invalid algo");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Self test for Cipher algorithms
|
|
*/
|
|
int _fips_qcedev_cipher_selftest(struct qcedev_control *podev)
|
|
{
|
|
int ret = 0, tv_index = 0, num_tv;
|
|
struct qcedev_async_req qcedev_areq;
|
|
struct qce_req creq;
|
|
struct scatterlist fips_sg;
|
|
uint8_t *k_align_src = NULL;
|
|
|
|
/* initiate handle */
|
|
if (_fips_initiate_qcedev_handle(podev, &qcedev_areq))
|
|
return -ENOMEM;
|
|
|
|
num_tv = (sizeof(fips_test_vector_cipher)) /
|
|
(sizeof(struct _fips_test_vector_cipher));
|
|
|
|
/* tests one by one */
|
|
for (tv_index = 0; tv_index < num_tv; tv_index++) {
|
|
|
|
/* Allocate single buffer for in-place operation */
|
|
k_align_src = kzalloc(QCE_MAX_OPER_DATA, GFP_KERNEL);
|
|
if (k_align_src == NULL) {
|
|
pr_err("qcedev: Failed to allocate memory for k_align_src %ld\n",
|
|
PTR_ERR(k_align_src));
|
|
kzfree(qcedev_areq.handle);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/**************** Encryption Tests *****************/
|
|
init_completion(&qcedev_areq.complete);
|
|
memcpy(&k_align_src[0],
|
|
fips_test_vector_cipher[tv_index].pln_txt,
|
|
fips_test_vector_cipher[tv_index].pln_txt_len);
|
|
|
|
/* Initiate qcedev request */
|
|
_fips_initiate_qcedev_async_req_cipher(&qcedev_areq,
|
|
QCEDEV_OPER_ENC, &fips_sg,
|
|
k_align_src, tv_index);
|
|
podev->active_command = &qcedev_areq;
|
|
|
|
/* Initiate qce cipher request */
|
|
if (_fips_initiate_qce_req_cipher(&qcedev_areq,
|
|
&creq, QCE_ENCRYPT)) {
|
|
ret = -EINVAL;
|
|
kzfree(k_align_src);
|
|
goto free_handle;
|
|
}
|
|
|
|
/* qce call */
|
|
ret = qce_ablk_cipher_req(podev->qce, &creq);
|
|
if (ret == 0)
|
|
wait_for_completion(&qcedev_areq.complete);
|
|
else {
|
|
kzfree(k_align_src);
|
|
goto free_handle;
|
|
}
|
|
|
|
/* Known answer test for encryption */
|
|
if (memcmp(k_align_src,
|
|
fips_test_vector_cipher[tv_index].enc_txt,
|
|
fips_test_vector_cipher[tv_index].enc_txt_len)) {
|
|
ret = -1;
|
|
kzfree(k_align_src);
|
|
goto free_handle;
|
|
}
|
|
|
|
/**************** Decryption Tests *****************/
|
|
init_completion(&qcedev_areq.complete);
|
|
memset(&k_align_src[0], 0,
|
|
fips_test_vector_cipher[tv_index].pln_txt_len);
|
|
memcpy(&k_align_src[0],
|
|
fips_test_vector_cipher[tv_index].enc_txt,
|
|
fips_test_vector_cipher[tv_index].enc_txt_len);
|
|
|
|
/* Initiate qcedev request */
|
|
_fips_initiate_qcedev_async_req_cipher(&qcedev_areq,
|
|
QCEDEV_OPER_DEC, &fips_sg,
|
|
k_align_src, tv_index);
|
|
podev->active_command = &qcedev_areq;
|
|
|
|
/*Initiate qce cipher request */
|
|
if (_fips_initiate_qce_req_cipher(&qcedev_areq,
|
|
&creq, QCE_DECRYPT)) {
|
|
ret = -EINVAL;
|
|
kzfree(k_align_src);
|
|
goto free_handle;
|
|
}
|
|
|
|
/* qce call */
|
|
ret = qce_ablk_cipher_req(podev->qce, &creq);
|
|
if (ret == 0)
|
|
wait_for_completion(&qcedev_areq.complete);
|
|
else {
|
|
kzfree(k_align_src);
|
|
goto free_handle;
|
|
}
|
|
|
|
/* Known answer test for Decryption */
|
|
if (memcmp(k_align_src,
|
|
fips_test_vector_cipher[tv_index].pln_txt,
|
|
fips_test_vector_cipher[tv_index].pln_txt_len)) {
|
|
ret = -1;
|
|
kzfree(k_align_src);
|
|
goto free_handle;
|
|
}
|
|
podev->active_command = NULL;
|
|
kzfree(k_align_src);
|
|
}
|
|
|
|
free_handle:
|
|
kzfree(qcedev_areq.handle);
|
|
return ret;
|
|
}
|
|
|
|
void fips_reg_drbg_callback(void *src)
|
|
{
|
|
drbg_call_back = src;
|
|
}
|
|
EXPORT_SYMBOL(fips_reg_drbg_callback);
|