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 
15 #include <net/mac80211.h>
16 #include "key.h"
17 #include "aes_ccm.h"
18 
aes_ccm_prepare(struct crypto_cipher * tfm,u8 * scratch,u8 * a)19 static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
20 {
21 	int i;
22 	u8 *b_0, *aad, *b, *s_0;
23 
24 	b_0 = scratch + 3 * AES_BLOCK_LEN;
25 	aad = scratch + 4 * AES_BLOCK_LEN;
26 	b = scratch;
27 	s_0 = scratch + AES_BLOCK_LEN;
28 
29 	crypto_cipher_encrypt_one(tfm, b, b_0);
30 
31 	/* Extra Authenticate-only data (always two AES blocks) */
32 	for (i = 0; i < AES_BLOCK_LEN; i++)
33 		aad[i] ^= b[i];
34 	crypto_cipher_encrypt_one(tfm, b, aad);
35 
36 	aad += AES_BLOCK_LEN;
37 
38 	for (i = 0; i < AES_BLOCK_LEN; i++)
39 		aad[i] ^= b[i];
40 	crypto_cipher_encrypt_one(tfm, a, aad);
41 
42 	/* Mask out bits from auth-only-b_0 */
43 	b_0[0] &= 0x07;
44 
45 	/* S_0 is used to encrypt T (= MIC) */
46 	b_0[14] = 0;
47 	b_0[15] = 0;
48 	crypto_cipher_encrypt_one(tfm, s_0, b_0);
49 }
50 
51 
ieee80211_aes_ccm_encrypt(struct crypto_cipher * tfm,u8 * scratch,u8 * data,size_t data_len,u8 * cdata,u8 * mic)52 void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
53 			       u8 *data, size_t data_len,
54 			       u8 *cdata, u8 *mic)
55 {
56 	int i, j, last_len, num_blocks;
57 	u8 *pos, *cpos, *b, *s_0, *e, *b_0, *aad;
58 
59 	b = scratch;
60 	s_0 = scratch + AES_BLOCK_LEN;
61 	e = scratch + 2 * AES_BLOCK_LEN;
62 	b_0 = scratch + 3 * AES_BLOCK_LEN;
63 	aad = scratch + 4 * AES_BLOCK_LEN;
64 
65 	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
66 	last_len = data_len % AES_BLOCK_LEN;
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_LEN;
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, *aad;
98 
99 	b = scratch;
100 	s_0 = scratch + AES_BLOCK_LEN;
101 	a = scratch + 2 * AES_BLOCK_LEN;
102 	b_0 = scratch + 3 * AES_BLOCK_LEN;
103 	aad = scratch + 4 * AES_BLOCK_LEN;
104 
105 	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
106 	last_len = data_len % AES_BLOCK_LEN;
107 	aes_ccm_prepare(tfm, scratch, a);
108 
109 	/* Process payload blocks */
110 	cpos = cdata;
111 	pos = data;
112 	for (j = 1; j <= num_blocks; j++) {
113 		int blen = (j == num_blocks && last_len) ?
114 			last_len : AES_BLOCK_LEN;
115 
116 		/* Decryption followed by authentication */
117 		b_0[14] = (j >> 8) & 0xff;
118 		b_0[15] = j & 0xff;
119 		crypto_cipher_encrypt_one(tfm, b, b_0);
120 		for (i = 0; i < blen; i++) {
121 			*pos = *cpos++ ^ b[i];
122 			a[i] ^= *pos++;
123 		}
124 		crypto_cipher_encrypt_one(tfm, a, a);
125 	}
126 
127 	for (i = 0; i < CCMP_MIC_LEN; i++) {
128 		if ((mic[i] ^ s_0[i]) != a[i])
129 			return -1;
130 	}
131 
132 	return 0;
133 }
134 
135 
ieee80211_aes_key_setup_encrypt(const u8 key[])136 struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
137 {
138 	struct crypto_cipher *tfm;
139 
140 	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
141 	if (!IS_ERR(tfm))
142 		crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN);
143 
144 	return tfm;
145 }
146 
147 
ieee80211_aes_key_free(struct crypto_cipher * tfm)148 void ieee80211_aes_key_free(struct crypto_cipher *tfm)
149 {
150 	crypto_free_cipher(tfm);
151 }
152