Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | /*
* Copyright 2003-2004, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/err.h>
#include <crypto/aes.h>
#include <net/mac80211.h>
#include "key.h"
#include "aes_ccm.h"
static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
{
int i;
u8 *b_0, *aad, *b, *s_0;
b_0 = scratch + 3 * AES_BLOCK_SIZE;
aad = scratch + 4 * AES_BLOCK_SIZE;
b = scratch;
s_0 = scratch + AES_BLOCK_SIZE;
crypto_cipher_encrypt_one(tfm, b, b_0);
/* Extra Authenticate-only data (always two AES blocks) */
for (i = 0; i < AES_BLOCK_SIZE; i++)
aad[i] ^= b[i];
crypto_cipher_encrypt_one(tfm, b, aad);
aad += AES_BLOCK_SIZE;
for (i = 0; i < AES_BLOCK_SIZE; i++)
aad[i] ^= b[i];
crypto_cipher_encrypt_one(tfm, a, aad);
/* Mask out bits from auth-only-b_0 */
b_0[0] &= 0x07;
/* S_0 is used to encrypt T (= MIC) */
b_0[14] = 0;
b_0[15] = 0;
crypto_cipher_encrypt_one(tfm, s_0, b_0);
}
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
u8 *data, size_t data_len,
u8 *cdata, u8 *mic)
{
int i, j, last_len, num_blocks;
u8 *pos, *cpos, *b, *s_0, *e, *b_0;
b = scratch;
s_0 = scratch + AES_BLOCK_SIZE;
e = scratch + 2 * AES_BLOCK_SIZE;
b_0 = scratch + 3 * AES_BLOCK_SIZE;
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
last_len = data_len % AES_BLOCK_SIZE;
aes_ccm_prepare(tfm, scratch, b);
/* Process payload blocks */
pos = data;
cpos = cdata;
for (j = 1; j <= num_blocks; j++) {
int blen = (j == num_blocks && last_len) ?
last_len : AES_BLOCK_SIZE;
/* Authentication followed by encryption */
for (i = 0; i < blen; i++)
b[i] ^= pos[i];
crypto_cipher_encrypt_one(tfm, b, b);
b_0[14] = (j >> 8) & 0xff;
b_0[15] = j & 0xff;
crypto_cipher_encrypt_one(tfm, e, b_0);
for (i = 0; i < blen; i++)
*cpos++ = *pos++ ^ e[i];
}
for (i = 0; i < CCMP_MIC_LEN; i++)
mic[i] = b[i] ^ s_0[i];
}
int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
u8 *cdata, size_t data_len, u8 *mic, u8 *data)
{
int i, j, last_len, num_blocks;
u8 *pos, *cpos, *b, *s_0, *a, *b_0;
b = scratch;
s_0 = scratch + AES_BLOCK_SIZE;
a = scratch + 2 * AES_BLOCK_SIZE;
b_0 = scratch + 3 * AES_BLOCK_SIZE;
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
last_len = data_len % AES_BLOCK_SIZE;
aes_ccm_prepare(tfm, scratch, a);
/* Process payload blocks */
cpos = cdata;
pos = data;
for (j = 1; j <= num_blocks; j++) {
int blen = (j == num_blocks && last_len) ?
last_len : AES_BLOCK_SIZE;
/* Decryption followed by authentication */
b_0[14] = (j >> 8) & 0xff;
b_0[15] = j & 0xff;
crypto_cipher_encrypt_one(tfm, b, b_0);
for (i = 0; i < blen; i++) {
*pos = *cpos++ ^ b[i];
a[i] ^= *pos++;
}
crypto_cipher_encrypt_one(tfm, a, a);
}
for (i = 0; i < CCMP_MIC_LEN; i++) {
if ((mic[i] ^ s_0[i]) != a[i])
return -1;
}
return 0;
}
struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
{
struct crypto_cipher *tfm;
tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
if (!IS_ERR(tfm))
crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN);
return tfm;
}
void ieee80211_aes_key_free(struct crypto_cipher *tfm)
{
crypto_free_cipher(tfm);
}
|