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