blob: 407310bd99ad8a6bbc84bbab59fef7b12883a47f [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.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*
* $File: //depot/software/projects/feature_branches/gen5_phase1/os/linux/classic/ap/apps/ssm/lib/aniSsmAesKeyWrap.c $
*
* Contains definitions for the AES Key Wrap algorithm from RFC 3394.
*
* Author: Mayank D. Upadhyay
* Date: 31-March-2003
* History:-
* Date Modified by Modification Information
* ------------------------------------------------------
*
*/
#include "vos_types.h"
#include "bapRsnSsmServices.h"
#include "bapRsnSsmEapol.h"
#include "bapRsnErrors.h"
#include "bapInternal.h"
#include "bapRsn8021xFsm.h"
#include "bapRsn8021xAuthFsm.h"
#include "vos_utils.h"
#include "vos_memory.h"
#include "vos_timer.h"
#include "bapRsnTxRx.h"
#include "bapRsnSsmAesKeyWrap.h"
#if 0
#include <assert.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <aniAsfHdr.h>
#include <aniUtils.h>
#include <aniErrors.h>
#include <aniAsfLog.h>
#include "aniSsmAesKeyWrap.h"
#include "aniSsmUtils.h"
#endif
#define ANI_SSM_AES_KEY_WRAP_IC_SIZE ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE
typedef struct tag_aes_key {
tANI_U32 eK[64], dK[64];
int Nr;
} AES_KEY;
static tANI_U8 gAniSsmAesKeyWrapIv[] = {
0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6
};
static int
aes(v_U32_t cryptHandle, tANI_U8 *keyBytes, tANI_U32 keyLen,
tANI_U8 a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 ri[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 b[AES_BLOCK_SIZE]);
static int
aes_1(v_U32_t cryptHandle, tANI_U8 *keyBytes, tANI_U32 keyLen,
tANI_U8 at[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 ri[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 b[AES_BLOCK_SIZE]);
static int
xor(tANI_U8 a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE], tANI_U32 t);
/**
* Implements the AES Key Wrap algorithm described in RFC 3394.
* If n is the number of blocks in plainText, of size
* ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE, then the output value is (n+1)
* blocks. The first block is the IV from section 2.2.3 o the
* RFC. Note: It is the caller's responsibility to free the returned
* value.
*
* @param plainText the plaintext data to wrap
* @param len the length of the plaintext, which must be a multiple of
* ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE.
* @param keyEncKey the encryption key
* @param keyEncKeyLen the length of keyEncKey
* @param cipherTextPtr is set to a newly allocated array containing
* the result if the operation succeeds. It is the caller's
* responsibility to free this.
*
* @return ANI_OK if the operation succeeds
*/
int
aniSsmAesKeyWrap(v_U32_t cryptHandle, tANI_U8 *plainText, tANI_U32 len,
tANI_U8 *keyEncKey, tANI_U32 keyEncKeyLen,
tANI_U8 **cipherTextPtr)
{
int i, j, n;
int retVal;
tANI_U8 a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE];
tANI_U8 *r = NULL;
tANI_U32 t;
tANI_U8 b[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE*2];
n = len / ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE;
if ((len % ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE) != 0) {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Illegal number of input bytes to AES Key Wrap!");
return ANI_E_ILLEGAL_ARG;
}
// Allocate enough storage for 'A' as well as 'R'
r = vos_mem_malloc((n + 1) * ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
if (r == NULL) {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Could not allocate space for R");
return ANI_E_MALLOC_FAILED;
}
vos_mem_copy(a, gAniSsmAesKeyWrapIv, sizeof(a));
vos_mem_copy(r + ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE, plainText, len);
for (j = 0; j <= 5; j++) {
for (i = 1; i <= n; i++) {
retVal = aes(cryptHandle, keyEncKey, keyEncKeyLen,
a,
r + i*ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
b);
if( !ANI_IS_STATUS_SUCCESS( retVal) ) goto error;
vos_mem_copy(a, b, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
t = n*j + i;
xor(a, t);
vos_mem_copy(r + i*ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
b + sizeof(b) - ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
}
}
vos_mem_copy(r, a, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
*cipherTextPtr = r;
return ANI_OK;
error:
if (r != NULL)
vos_mem_free(r);
return retVal;
}
/**
* Implements the AES Key Unwrap algorithm described in RFC 3394.
* If (n+1) is the number of blocks in cipherText, of size
* ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE, then the output value is (n+1)
* blocks. The actual plaintext consists of n blocks that start at the
* second block. Note: It is the caller's responsibility to free the
* returned value.
*
* @param cipherText the cipertext data to unwrap
* @param len the length of the ciphertext, which must be a multiple of
* ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE.
* @param keyEncKey the encryption key
* @param keyEncKeyLen the length of keyEncKey
* @param plainTextPtr is set to a newly allocated array containing
* the result if the operation succeeds. It is the caller's
* responsibility to free this.
*
* @return ANI_OK if the operation succeeds
*/
int
aniSsmAesKeyUnwrap(v_U32_t cryptHandle, tANI_U8 *cipherText, tANI_U32 len,
tANI_U8 *keyEncKey, tANI_U32 keyEncKeyLen,
tANI_U8 **plainTextPtr)
{
int i, j;
int retVal;
tANI_U8 a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE];
tANI_U8 *r = NULL;
tANI_U32 n;
tANI_U32 t;
tANI_U8 b[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE*2];
n = len / ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE - 1;
if ((len % ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE) != 0) {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Illegal number of input bytes to AES Key Unwrap!");
return ANI_E_ILLEGAL_ARG;
}
// Allocate enough storage for 'A' as well as 'R'
r = vos_mem_malloc((n + 1) * ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
if (r == NULL) {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Could not allocate space for R");
return ANI_E_MALLOC_FAILED;
}
vos_mem_copy(a, cipherText, sizeof(a));
vos_mem_copy(r + ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
cipherText + ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
len - ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
for (j = 5; j >= 0; j--) {
for (i = n; i >= 1; i--) {
t = n*j + i;
xor(a, t);
retVal = aes_1(cryptHandle, keyEncKey, keyEncKeyLen,
a,
r + i*ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
b);
if( !ANI_IS_STATUS_SUCCESS( retVal) ) goto error;
vos_mem_copy(a, b, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
vos_mem_copy(r + i*ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
b + sizeof(b) - ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
}
}
if (vos_mem_compare2(a, gAniSsmAesKeyWrapIv, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE) != 0) {
retVal = ANI_E_MIC_FAILED;
goto error;
}
*plainTextPtr = r;
return ANI_OK;
error:
if (r != NULL)
vos_mem_free(r);
return retVal;
}
static int
aes(v_U32_t cryptHandle, tANI_U8 *keyBytes, tANI_U32 keyLen,
tANI_U8 a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 ri[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 b[AES_BLOCK_SIZE]) {
int retVal = 0;
// AES_KEY aesKey;
tANI_U8 in[AES_BLOCK_SIZE];
tANI_U8 *out;
VOS_ASSERT (AES_BLOCK_SIZE == ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE*2);
// Concatenate A and R[i]
vos_mem_copy(in, a, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
vos_mem_copy(in + ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
ri, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
out = b;
#if 0
retVal = AES_set_encrypt_key(keyBytes, keyLen*8, &aesKey);
if (retVal != 0) {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"AES_set_encrypt_key returned %d", retVal);
return ANI_E_FAILED;
}
AES_encrypt(in, out, &aesKey);
#else // Enable to use VOS function
retVal = vos_encrypt_AES(cryptHandle, /* Handle */
in, /* input */
out, /* output */
keyBytes); /* key */
if (retVal != 0) {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"vos_encrypt_AES returned %d", retVal);
return ANI_E_FAILED;
}
#endif
return ANI_OK;
}
static int
aes_1(v_U32_t cryptHandle, tANI_U8 *keyBytes, tANI_U32 keyLen,
tANI_U8 at[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 ri[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE],
tANI_U8 b[AES_BLOCK_SIZE]) {
int retVal;
// AES_KEY aesKey;
tANI_U8 in[AES_BLOCK_SIZE];
tANI_U8 *out;
VOS_ASSERT (AES_BLOCK_SIZE == ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE*2);
// Concatenate A and R[i]
vos_mem_copy(in, at, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
vos_mem_copy(in + ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE,
ri, ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE);
out = b;
#if 0
retVal = AES_set_decrypt_key(keyBytes, keyLen*8, &aesKey);
if (retVal != 0) {
ANI_SSM_LOG_E("AES_set_encrypt_key returned %d", retVal);
assert(0 && "AES_set_encrypt_key failed!");
return ANI_E_FAILED;
}
AES_decrypt(in, out, &aesKey);
#else
retVal = vos_decrypt_AES(cryptHandle, /* Handle */
in, /* input */
out, /* output */
keyBytes); /* key */
if (retVal != 0) {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"vos_decrypt_AES returned %d", retVal);
}
#endif
return ANI_OK;
}
// From File : aniAsfHdr.h
/*
* Put a long in host order into a char array in network order.
*
*/
static inline char *aniAsfWr32(char *cp, tANI_U32 x)
{
tAniU32ValAry r;
int i;
r.val = vos_cpu_to_be32(x);
i = sizeof(tANI_U32) - 1;
cp[3] = r.ary[i--];
cp[2] = r.ary[i--];
cp[1] = r.ary[i--];
cp[0] = r.ary[i];
return (cp + sizeof(tANI_U32));
}
// From file : aniAsfMisc.c
/*
* Put a long in host order into a char array in network order.
*
*/
char *aniAsfPut32(char *cp, tANI_U32 x)
{
return(aniAsfWr32(cp, x));
}
static int
xor(tANI_U8 a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE], tANI_U32 t)
{
tANI_U8 tmp[4];
aniAsfPut32((char *)tmp, t);
a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE-1] ^= tmp[3];
a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE-2] ^= tmp[2];
a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE-3] ^= tmp[1];
a[ANI_SSM_AES_KEY_WRAP_BLOCK_SIZE-4] ^= tmp[0];
return ANI_OK;
}