blob: 381943a6d911af10cb0b55f385968801f76c6000 [file] [log] [blame]
/* Copyright (c) 2011-2013, 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/export.h>
#include <linux/qcomwlan_secif.h>
#include <crypto/aes.h>
/* APIs for calling crypto routines from kernel
*/
struct crypto_ahash *wcnss_wlan_crypto_alloc_ahash(const char *alg_name,
u32 type, u32 mask)
{
return crypto_alloc_ahash(alg_name, type, mask);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ahash);
int wcnss_wlan_crypto_ahash_digest(struct ahash_request *req)
{
return crypto_ahash_digest(req);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_digest);
void wcnss_wlan_crypto_free_ahash(struct crypto_ahash *tfm)
{
crypto_free_ahash(tfm);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_free_ahash);
int wcnss_wlan_crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int keylen)
{
return crypto_ahash_setkey(tfm, key, keylen);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_setkey);
struct crypto_ablkcipher *
wcnss_wlan_crypto_alloc_ablkcipher(const char *alg_name, u32 type, u32 mask)
{
return crypto_alloc_ablkcipher(alg_name, type, mask);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ablkcipher);
void wcnss_wlan_ablkcipher_request_free(struct ablkcipher_request *req)
{
ablkcipher_request_free(req);
}
EXPORT_SYMBOL(wcnss_wlan_ablkcipher_request_free);
void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm)
{
crypto_free_ablkcipher(tfm);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_free_ablkcipher);
void wcnss_wlan_crypto_free_cipher(struct crypto_cipher *tfm)
{
crypto_free_cipher(tfm);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_free_cipher);
struct crypto_cipher *
wcnss_wlan_crypto_alloc_cipher(const char *alg_name, u32 type, u32 mask)
{
return crypto_alloc_cipher(alg_name, type, mask);
}
EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_cipher);
static inline void xor_128(const u8 *a, const u8 *b, u8 *out)
{
u8 i;
for (i = 0; i < AES_BLOCK_SIZE; i++)
out[i] = a[i] ^ b[i];
}
static inline void leftshift_onebit(const u8 *input, u8 *output)
{
int i, overflow = 0;
for (i = (AES_BLOCK_SIZE - 1); i >= 0; i--) {
output[i] = input[i] << 1;
output[i] |= overflow;
overflow = (input[i] & 0x80) ? 1 : 0;
}
return;
}
static void generate_subkey(struct crypto_cipher *tfm, u8 *k1, u8 *k2)
{
u8 l[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
u8 const_rb[AES_BLOCK_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87};
u8 const_zero[AES_BLOCK_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
crypto_cipher_encrypt_one(tfm, l, const_zero);
if ((l[0] & 0x80) == 0) { /* If MSB(l) = 0, then k1 = l << 1 */
leftshift_onebit(l, k1);
} else { /* Else k1 = ( l << 1 ) (+) Rb */
leftshift_onebit(l, tmp);
xor_128(tmp, const_rb, k1);
}
if ((k1[0] & 0x80) == 0) {
leftshift_onebit(k1, k2);
} else {
leftshift_onebit(k1, tmp);
xor_128(tmp, const_rb, k2);
}
}
static inline void padding(u8 *lastb, u8 *pad, u16 length)
{
u8 j;
/* original last block */
for (j = 0; j < AES_BLOCK_SIZE; j++) {
if (j < length)
pad[j] = lastb[j];
else if (j == length)
pad[j] = 0x80;
else
pad[j] = 0x00;
}
}
void wcnss_wlan_cmac_calc_mic(struct crypto_cipher *tfm, u8 *m,
u16 length, u8 *mac)
{
u8 x[AES_BLOCK_SIZE], y[AES_BLOCK_SIZE];
u8 m_last[AES_BLOCK_SIZE], padded[AES_BLOCK_SIZE];
u8 k1[AES_KEYSIZE_128], k2[AES_KEYSIZE_128];
int cmpBlk;
int i, nBlocks = (length + 15)/AES_BLOCK_SIZE;
generate_subkey(tfm, k1, k2);
if (nBlocks == 0) {
nBlocks = 1;
cmpBlk = 0;
} else {
cmpBlk = ((length % AES_BLOCK_SIZE) == 0) ? 1 : 0;
}
if (cmpBlk) { /* Last block is complete block */
xor_128(&m[AES_BLOCK_SIZE * (nBlocks - 1)], k1, m_last);
} else { /* Last block is not complete block */
padding(&m[AES_BLOCK_SIZE * (nBlocks - 1)], padded,
length % AES_BLOCK_SIZE);
xor_128(padded, k2, m_last);
}
for (i = 0; i < AES_BLOCK_SIZE; i++)
x[i] = 0;
for (i = 0; i < (nBlocks - 1); i++) {
xor_128(x, &m[AES_BLOCK_SIZE * i], y); /* y = Mi (+) x */
crypto_cipher_encrypt_one(tfm, x, y); /* x = AES-128(KEY, y) */
}
xor_128(x, m_last, y);
crypto_cipher_encrypt_one(tfm, x, y);
memcpy(mac, x, CMAC_TLEN);
}
EXPORT_SYMBOL(wcnss_wlan_cmac_calc_mic);