| /* 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); |