blob: 958b2cc1efe36c433eafad187ee289b25734b79b [file] [log] [blame]
/*
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* */
/*============================================================================
FILE: vos_utils.c
OVERVIEW: This source file contains definitions for vOS crypto APIs
The four APIs mentioned in this file are used for
initializing, and de-initializing a crypto context, and
obtaining truly random data (for keys), as well as
SHA1 HMAC, and AES encrypt and decrypt routines.
The routines include:
vos_crypto_init() - Initializes Crypto module
vos_crypto_deinit() - De-initializes Crypto module
vos_rand_get_bytes() - Generates random byte
vos_sha1_hmac_str() - Generate the HMAC-SHA1 of a string given a key
vos_encrypt_AES() - Generate AES Encrypted byte stream
vos_decrypt_AES() - Decrypts an AES Encrypted byte stream
DEPENDENCIES:
Copyright (c) 2007 QUALCOMM Incorporated.
All Rights Reserved.
Qualcomm Confidential and Proprietary
============================================================================*/
/*============================================================================
EDIT HISTORY FOR MODULE
============================================================================*/
/*----------------------------------------------------------------------------
* Include Files
* -------------------------------------------------------------------------*/
#include "vos_trace.h"
#include "vos_utils.h"
#include "vos_memory.h"
#include <linux/err.h>
#include <linux/random.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/completion.h>
#include <crypto/hash.h>
/*----------------------------------------------------------------------------
* Preprocessor Definitions and Constants
* -------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
* Type Declarations
* -------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
* Global Data Definitions
* -------------------------------------------------------------------------*/
extern struct crypto_ahash *wcnss_wlan_crypto_alloc_ahash(const char *alg_name,
unsigned int type,
unsigned int mask);
extern int wcnss_wlan_crypto_ahash_digest(struct ahash_request *req);
extern void wcnss_wlan_crypto_free_ahash(struct crypto_ahash *tfm);
extern int wcnss_wlan_crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int keylen);
extern struct crypto_ablkcipher *wcnss_wlan_crypto_alloc_ablkcipher(const char *alg_name,
u32 type, u32 mask);
extern void wcnss_wlan_ablkcipher_request_free(struct ablkcipher_request *req);
extern void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm);
/*----------------------------------------------------------------------------
* Static Variable Definitions
* -------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
Function Definitions and Documentation
* -------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------
\brief vos_crypto_init() - Initializes Crypto module
The vos_crypto_init() function initializes Crypto module.
\param phCryptProv - pointer to the Crypt handle
\return VOS_STATUS_SUCCESS - Successfully generated random memory.
VOS_STATUS_E_FAULT - pbBuf is an invalid pointer.
VOS_STATUS_E_FAILURE - default return value if it fails due to
unknown reasons
***VOS_STATUS_E_RESOURCES - System resources (other than memory)
are unavailable
\sa
( *** return value not considered yet )
--------------------------------------------------------------------------*/
VOS_STATUS vos_crypto_init( v_U32_t *phCryptProv )
{
VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
// This implementation doesn't require a crypto context
*phCryptProv = (v_U32_t)NULL;
uResult = VOS_STATUS_SUCCESS;
return ( uResult );
}
VOS_STATUS vos_crypto_deinit( v_U32_t hCryptProv )
{
VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
// CryptReleaseContext succeeded
uResult = VOS_STATUS_SUCCESS;
return ( uResult );
}
/*--------------------------------------------------------------------------
\brief vos_rand_get_bytes() - Generates random byte
The vos_rand_get_bytes() function generate random bytes.
Buffer should be allocated before calling vos_rand_get_bytes().
Attempting to initialize an already initialized lock results in
a failure.
\param lock - pointer to the opaque lock object to initialize
\return VOS_STATUS_SUCCESS - Successfully generated random memory.
VOS_STATUS_E_FAULT - pbBuf is an invalid pointer.
VOS_STATUS_E_FAILURE - default return value if it fails due to
unknown reasons
***VOS_STATUS_E_RESOURCES - System resources (other than memory)
are unavailable
\sa
( *** return value not considered yet )
--------------------------------------------------------------------------*/
VOS_STATUS vos_rand_get_bytes( v_U32_t cryptHandle, v_U8_t *pbBuf, v_U32_t numBytes )
{
VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
//v_UINT_t uCode;
// HCRYPTPROV hCryptProv = (HCRYPTPROV) cryptHandle;
//check for invalid pointer
if ( NULL == pbBuf )
{
uResult = VOS_STATUS_E_FAULT;
return ( uResult );
}
//#if 0
// get_random_bytes() is a void procedure
get_random_bytes( pbBuf, numBytes);
// "Random sequence generated."
uResult = VOS_STATUS_SUCCESS;
//#endif
return ( uResult );
}
/**
* vos_sha1_hmac_str
*
* FUNCTION:
* Generate the HMAC-SHA1 of a string given a key.
*
* LOGIC:
* Standard HMAC processing from RFC 2104. The code is provided in the
* appendix of the RFC.
*
* ASSUMPTIONS:
* The RFC is correct.
*
* @param text text to be hashed
* @param textLen length of text
* @param key key to use for HMAC
* @param keyLen length of key
* @param digest holds resultant SHA1 HMAC (20B)
*
* @return VOS_STATUS_SUCCSS if the operation succeeds
*
*/
struct hmac_sha1_result {
struct completion completion;
int err;
};
static void hmac_sha1_complete(struct crypto_async_request *req, int err)
{
struct hmac_sha1_result *r = req->data;
if (err == -EINPROGRESS)
return;
r->err = err;
complete(&r->completion);
}
int hmac_sha1(v_U8_t *key, v_U8_t ksize, char *plaintext, v_U8_t psize,
v_U8_t *output, v_U8_t outlen)
{
int ret = 0;
struct crypto_ahash *tfm;
struct scatterlist sg;
struct ahash_request *req;
struct hmac_sha1_result tresult;
void *hash_buff = NULL;
unsigned char hash_result[64];
int i;
memset(output, 0, outlen);
init_completion(&tresult.completion);
tfm = wcnss_wlan_crypto_alloc_ahash("hmac(sha1)", CRYPTO_ALG_TYPE_AHASH,
CRYPTO_ALG_TYPE_AHASH_MASK);
if (IS_ERR(tfm)) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ahash failed");
ret = PTR_ERR(tfm);
goto err_tfm;
}
req = ahash_request_alloc(tfm, GFP_KERNEL);
if (!req) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to allocate request for hmac(sha1)");
ret = -ENOMEM;
goto err_req;
}
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
hmac_sha1_complete, &tresult);
hash_buff = kzalloc(psize, GFP_KERNEL);
if (!hash_buff) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to kzalloc hash_buff");
ret = -ENOMEM;
goto err_hash_buf;
}
memset(hash_result, 0, 64);
memcpy(hash_buff, plaintext, psize);
sg_init_one(&sg, hash_buff, psize);
if (ksize) {
crypto_ahash_clear_flags(tfm, ~0);
ret = wcnss_wlan_crypto_ahash_setkey(tfm, key, ksize);
if (ret) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_ahash_setkey failed");
goto err_setkey;
}
}
ahash_request_set_crypt(req, &sg, hash_result, psize);
ret = wcnss_wlan_crypto_ahash_digest(req);
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "ret 0x%x");
switch (ret) {
case 0:
for (i=0; i< outlen; i++)
output[i] = hash_result[i];
break;
case -EINPROGRESS:
case -EBUSY:
ret = wait_for_completion_interruptible(&tresult.completion);
if (!ret && !tresult.err) {
INIT_COMPLETION(tresult.completion);
break;
} else {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "wait_for_completion_interruptible failed");
if (!ret)
ret = tresult.err;
goto out;
}
default:
goto out;
}
out:
err_setkey:
kfree(hash_buff);
err_hash_buf:
ahash_request_free(req);
err_req:
wcnss_wlan_crypto_free_ahash(tfm);
err_tfm:
return ret;
}
VOS_STATUS vos_sha1_hmac_str(v_U32_t cryptHandle, /* Handle */
v_U8_t *pText, /* pointer to data stream */
v_U32_t textLen, /* length of data stream */
v_U8_t *pKey, /* pointer to authentication key */
v_U32_t keyLen, /* length of authentication key */
v_U8_t digest[VOS_DIGEST_SHA1_SIZE])/* caller digest to be filled in */
{
int ret = 0;
ret = hmac_sha1(
pKey, //v_U8_t *key,
(v_U8_t) keyLen, //v_U8_t ksize,
(char *)pText, //char *plaintext,
(v_U8_t) textLen, //v_U8_t psize,
digest, //v_U8_t *output,
VOS_DIGEST_SHA1_SIZE //v_U8_t outlen
);
if (ret != 0) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"hmac_sha1() call failed");
return VOS_STATUS_E_FAULT;
}
return VOS_STATUS_SUCCESS;
}
/**
* vos_md5_hmac_str
*
* FUNCTION:
* Generate the HMAC-MD5 of a string given a key.
*
* LOGIC:
* Standard HMAC processing from RFC 2104. The code is provided in the
* appendix of the RFC.
*
* ASSUMPTIONS:
* The RFC is correct.
*
* @param text text to be hashed
* @param textLen length of text
* @param key key to use for HMAC
* @param keyLen length of key
* @param digest holds resultant MD5 HMAC (20B)
*
* @return VOS_STATUS_SUCCSS if the operation succeeds
*
*/
struct hmac_md5_result {
struct completion completion;
int err;
};
static void hmac_md5_complete(struct crypto_async_request *req, int err)
{
struct hmac_md5_result *r = req->data;
if (err == -EINPROGRESS)
return;
r->err = err;
complete(&r->completion);
}
int hmac_md5(v_U8_t *key, v_U8_t ksize, char *plaintext, v_U8_t psize,
v_U8_t *output, v_U8_t outlen)
{
int ret = 0;
struct crypto_ahash *tfm;
struct scatterlist sg;
struct ahash_request *req;
struct hmac_md5_result tresult;
void *hash_buff = NULL;
unsigned char hash_result[64];
int i;
memset(output, 0, outlen);
init_completion(&tresult.completion);
tfm = wcnss_wlan_crypto_alloc_ahash("hmac(md5)", CRYPTO_ALG_TYPE_AHASH,
CRYPTO_ALG_TYPE_AHASH_MASK);
if (IS_ERR(tfm)) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ahash failed");
ret = PTR_ERR(tfm);
goto err_tfm;
}
req = ahash_request_alloc(tfm, GFP_KERNEL);
if (!req) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to allocate request for hmac(md5)");
ret = -ENOMEM;
goto err_req;
}
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
hmac_md5_complete, &tresult);
hash_buff = kzalloc(psize, GFP_KERNEL);
if (!hash_buff) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "failed to kzalloc hash_buff");
ret = -ENOMEM;
goto err_hash_buf;
}
memset(hash_result, 0, 64);
memcpy(hash_buff, plaintext, psize);
sg_init_one(&sg, hash_buff, psize);
if (ksize) {
crypto_ahash_clear_flags(tfm, ~0);
ret = wcnss_wlan_crypto_ahash_setkey(tfm, key, ksize);
if (ret) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_ahash_setkey failed");
goto err_setkey;
}
}
ahash_request_set_crypt(req, &sg, hash_result, psize);
ret = wcnss_wlan_crypto_ahash_digest(req);
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "ret 0x%x");
switch (ret) {
case 0:
for (i=0; i< outlen; i++)
output[i] = hash_result[i];
break;
case -EINPROGRESS:
case -EBUSY:
ret = wait_for_completion_interruptible(&tresult.completion);
if (!ret && !tresult.err) {
INIT_COMPLETION(tresult.completion);
break;
} else {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "wait_for_completion_interruptible failed");
if (!ret)
ret = tresult.err;
goto out;
}
default:
goto out;
}
out:
err_setkey:
kfree(hash_buff);
err_hash_buf:
ahash_request_free(req);
err_req:
wcnss_wlan_crypto_free_ahash(tfm);
err_tfm:
return ret;
}
VOS_STATUS vos_md5_hmac_str(v_U32_t cryptHandle, /* Handle */
v_U8_t *pText, /* pointer to data stream */
v_U32_t textLen, /* length of data stream */
v_U8_t *pKey, /* pointer to authentication key */
v_U32_t keyLen, /* length of authentication key */
v_U8_t digest[VOS_DIGEST_MD5_SIZE])/* caller digest to be filled in */
{
int ret = 0;
ret = hmac_md5(
pKey, //v_U8_t *key,
(v_U8_t) keyLen, //v_U8_t ksize,
(char *)pText, //char *plaintext,
(v_U8_t) textLen, //v_U8_t psize,
digest, //v_U8_t *output,
VOS_DIGEST_MD5_SIZE //v_U8_t outlen
);
if (ret != 0) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"hmac_md5() call failed");
return VOS_STATUS_E_FAULT;
}
return VOS_STATUS_SUCCESS;
}
struct ecb_aes_result {
struct completion completion;
int err;
};
static void ecb_aes_complete(struct crypto_async_request *req, int err)
{
struct ecb_aes_result *r = req->data;
if (err == -EINPROGRESS)
return;
r->err = err;
complete(&r->completion);
}
/*--------------------------------------------------------------------------
\brief vos_encrypt_AES() - Generate AES Encrypted byte stream
The vos_encrypt_AES() function generates the encrypted byte stream for given text.
Buffer should be allocated before calling vos_rand_get_bytes().
Attempting to initialize an already initialized lock results in
a failure.
\param lock - pointer to the opaque lock object to initialize
\return VOS_STATUS_SUCCESS - Successfully generated random memory.
VOS_STATUS_E_FAULT - pbBuf is an invalid pointer.
VOS_STATUS_E_FAILURE - default return value if it fails due to
unknown reasons
***VOS_STATUS_E_RESOURCES - System resources (other than memory)
are unavailable
\sa
( *** return value not considered yet )
--------------------------------------------------------------------------*/
#define IV_SIZE_AES_128 16
#define KEY_SIZE_AES_128 16
#define AES_BLOCK_SIZE 16
VOS_STATUS vos_encrypt_AES(v_U32_t cryptHandle, /* Handle */
v_U8_t *pPlainText, /* pointer to data stream */
v_U8_t *pCiphertext,
v_U8_t *pKey) /* pointer to authentication key */
{
// VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
struct ecb_aes_result result;
struct ablkcipher_request *req;
struct crypto_ablkcipher *tfm;
int ret = 0;
char iv[IV_SIZE_AES_128];
struct scatterlist sg_in;
struct scatterlist sg_out;
init_completion(&result.completion);
tfm = wcnss_wlan_crypto_alloc_ablkcipher( "cbc(aes)", 0, 0);
if (IS_ERR(tfm)) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ablkcipher failed");
ret = PTR_ERR(tfm);
goto err_tfm;
}
req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
if (!req) {
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, "Failed to allocate request for cbc(aes)");
ret = -ENOMEM;
goto err_req;
}
ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
ecb_aes_complete, &result);
crypto_ablkcipher_clear_flags(tfm, ~0);
ret = crypto_ablkcipher_setkey(tfm, pKey, KEY_SIZE_AES_128);
if (ret) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_cipher_setkey failed");
goto err_setkey;
}
memset(iv, 0, IV_SIZE_AES_128);
sg_init_one(&sg_in, pPlainText, AES_BLOCK_SIZE);
sg_init_one(&sg_out, pCiphertext, AES_BLOCK_SIZE);
ablkcipher_request_set_crypt(req, &sg_in, &sg_out, AES_BLOCK_SIZE, iv);
crypto_ablkcipher_encrypt(req);
// -------------------------------------
err_setkey:
wcnss_wlan_ablkcipher_request_free(req);
err_req:
wcnss_wlan_crypto_free_ablkcipher(tfm);
err_tfm:
//return ret;
if (ret != 0) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"%s() call failed", __func__);
return VOS_STATUS_E_FAULT;
}
return VOS_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief vos_decrypt_AES() - Decrypts an AES Encrypted byte stream
The vos_decrypt_AES() function decrypts the encrypted byte stream.
Buffer should be allocated before calling vos_rand_get_bytes().
Attempting to initialize an already initialized lock results in
a failure.
\param lock - pointer to the opaque lock object to initialize
\return VOS_STATUS_SUCCESS - Successfully generated random memory.
VOS_STATUS_E_FAULT - pbBuf is an invalid pointer.
VOS_STATUS_E_FAILURE - default return value if it fails due to
unknown reasons
***VOS_STATUS_E_RESOURCES - System resources (other than memory)
are unavailable
\sa
( *** return value not considered yet )
--------------------------------------------------------------------------*/
VOS_STATUS vos_decrypt_AES(v_U32_t cryptHandle, /* Handle */
v_U8_t *pText, /* pointer to data stream */
v_U8_t *pDecrypted,
v_U8_t *pKey) /* pointer to authentication key */
{
// VOS_STATUS uResult = VOS_STATUS_E_FAILURE;
struct ecb_aes_result result;
struct ablkcipher_request *req;
struct crypto_ablkcipher *tfm;
int ret = 0;
char iv[IV_SIZE_AES_128];
struct scatterlist sg_in;
struct scatterlist sg_out;
init_completion(&result.completion);
tfm = wcnss_wlan_crypto_alloc_ablkcipher( "cbc(aes)", 0, 0);
if (IS_ERR(tfm)) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_alloc_ablkcipher failed");
ret = PTR_ERR(tfm);
goto err_tfm;
}
req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
if (!req) {
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, "Failed to allocate request for cbc(aes)");
ret = -ENOMEM;
goto err_req;
}
ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
ecb_aes_complete, &result);
crypto_ablkcipher_clear_flags(tfm, ~0);
ret = crypto_ablkcipher_setkey(tfm, pKey, KEY_SIZE_AES_128);
if (ret) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR, "crypto_cipher_setkey failed");
goto err_setkey;
}
memset(iv, 0, IV_SIZE_AES_128);
sg_init_one(&sg_in, pText, AES_BLOCK_SIZE);
sg_init_one(&sg_out, pDecrypted, AES_BLOCK_SIZE);
ablkcipher_request_set_crypt(req, &sg_in, &sg_out, AES_BLOCK_SIZE, iv);
crypto_ablkcipher_decrypt(req);
// -------------------------------------
err_setkey:
wcnss_wlan_ablkcipher_request_free(req);
err_req:
wcnss_wlan_crypto_free_ablkcipher(tfm);
err_tfm:
//return ret;
if (ret != 0) {
VOS_TRACE(VOS_MODULE_ID_VOSS,VOS_TRACE_LEVEL_ERROR,"%s() call failed", __func__);
return VOS_STATUS_E_FAULT;
}
return VOS_STATUS_SUCCESS;
}