aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2009-01-08 06:32:01 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-01-29 16:00:02 -0500
commit765cb46a3fc856245ea68a7c961ac87c77e4ae2d (patch)
tree210cb9cd260430221ddb3be9620ee8ae90ecee34
parentfb7333367632c67d8b6b06fb8d906cdabb11b02a (diff)
mac80211: 802.11w - Add BIP (AES-128-CMAC)
Implement Broadcast/Multicast Integrity Protocol for management frame protection. This patch adds the needed definitions for the new information element (MMIE) and implementation for the new "encryption" type (though, BIP is actually not encrypting data, it provides only integrity protection). These routines will be used by a follow-on patch that enables BIP for multicast/broadcast robust management frames. Signed-off-by: Jouni Malinen <j@w1.fi> Acked-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--include/linux/ieee80211.h10
-rw-r--r--net/mac80211/Makefile1
-rw-r--r--net/mac80211/aes_cmac.c135
-rw-r--r--net/mac80211/aes_cmac.h19
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/key.h10
-rw-r--r--net/mac80211/wpa.c125
-rw-r--r--net/mac80211/wpa.h5
8 files changed, 306 insertions, 1 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index d5165895f316..cceb9e86c744 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -655,6 +655,15 @@ struct ieee80211_mgmt {
655#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) 655#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
656 656
657 657
658/* Management MIC information element (IEEE 802.11w) */
659struct ieee80211_mmie {
660 u8 element_id;
661 u8 length;
662 __le16 key_id;
663 u8 sequence_number[6];
664 u8 mic[8];
665} __attribute__ ((packed));
666
658/* Control frames */ 667/* Control frames */
659struct ieee80211_rts { 668struct ieee80211_rts {
660 __le16 frame_control; 669 __le16 frame_control;
@@ -1018,6 +1027,7 @@ enum ieee80211_eid {
1018 WLAN_EID_HT_INFORMATION = 61, 1027 WLAN_EID_HT_INFORMATION = 61,
1019 /* 802.11i */ 1028 /* 802.11i */
1020 WLAN_EID_RSN = 48, 1029 WLAN_EID_RSN = 48,
1030 WLAN_EID_MMIE = 76 /* 802.11w */,
1021 WLAN_EID_WPA = 221, 1031 WLAN_EID_WPA = 221,
1022 WLAN_EID_GENERIC = 221, 1032 WLAN_EID_GENERIC = 221,
1023 WLAN_EID_VENDOR_SPECIFIC = 221, 1033 WLAN_EID_VENDOR_SPECIFIC = 221,
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 7d4971aa443f..5c6fadfb6a00 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -15,6 +15,7 @@ mac80211-y := \
15 michael.o \ 15 michael.o \
16 tkip.o \ 16 tkip.o \
17 aes_ccm.o \ 17 aes_ccm.o \
18 aes_cmac.o \
18 cfg.o \ 19 cfg.o \
19 rx.o \ 20 rx.o \
20 spectmgmt.o \ 21 spectmgmt.o \
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c
new file mode 100644
index 000000000000..3d097b3d7b62
--- /dev/null
+++ b/net/mac80211/aes_cmac.c
@@ -0,0 +1,135 @@
1/*
2 * AES-128-CMAC with TLen 16 for IEEE 802.11w BIP
3 * Copyright 2008, Jouni Malinen <j@w1.fi>
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_cmac.h"
18
19#define AES_BLOCK_SIZE 16
20#define AES_CMAC_KEY_LEN 16
21#define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */
22#define AAD_LEN 20
23
24
25static void gf_mulx(u8 *pad)
26{
27 int i, carry;
28
29 carry = pad[0] & 0x80;
30 for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
31 pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
32 pad[AES_BLOCK_SIZE - 1] <<= 1;
33 if (carry)
34 pad[AES_BLOCK_SIZE - 1] ^= 0x87;
35}
36
37
38static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch,
39 size_t num_elem,
40 const u8 *addr[], const size_t *len, u8 *mac)
41{
42 u8 *cbc, *pad;
43 const u8 *pos, *end;
44 size_t i, e, left, total_len;
45
46 cbc = scratch;
47 pad = scratch + AES_BLOCK_SIZE;
48
49 memset(cbc, 0, AES_BLOCK_SIZE);
50
51 total_len = 0;
52 for (e = 0; e < num_elem; e++)
53 total_len += len[e];
54 left = total_len;
55
56 e = 0;
57 pos = addr[0];
58 end = pos + len[0];
59
60 while (left >= AES_BLOCK_SIZE) {
61 for (i = 0; i < AES_BLOCK_SIZE; i++) {
62 cbc[i] ^= *pos++;
63 if (pos >= end) {
64 e++;
65 pos = addr[e];
66 end = pos + len[e];
67 }
68 }
69 if (left > AES_BLOCK_SIZE)
70 crypto_cipher_encrypt_one(tfm, cbc, cbc);
71 left -= AES_BLOCK_SIZE;
72 }
73
74 memset(pad, 0, AES_BLOCK_SIZE);
75 crypto_cipher_encrypt_one(tfm, pad, pad);
76 gf_mulx(pad);
77
78 if (left || total_len == 0) {
79 for (i = 0; i < left; i++) {
80 cbc[i] ^= *pos++;
81 if (pos >= end) {
82 e++;
83 pos = addr[e];
84 end = pos + len[e];
85 }
86 }
87 cbc[left] ^= 0x80;
88 gf_mulx(pad);
89 }
90
91 for (i = 0; i < AES_BLOCK_SIZE; i++)
92 pad[i] ^= cbc[i];
93 crypto_cipher_encrypt_one(tfm, pad, pad);
94 memcpy(mac, pad, CMAC_TLEN);
95}
96
97
98void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad,
99 const u8 *data, size_t data_len, u8 *mic)
100{
101 const u8 *addr[3];
102 size_t len[3];
103 u8 zero[CMAC_TLEN];
104
105 memset(zero, 0, CMAC_TLEN);
106 addr[0] = aad;
107 len[0] = AAD_LEN;
108 addr[1] = data;
109 len[1] = data_len - CMAC_TLEN;
110 addr[2] = zero;
111 len[2] = CMAC_TLEN;
112
113 aes_128_cmac_vector(tfm, scratch, 3, addr, len, mic);
114}
115
116
117struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[])
118{
119 struct crypto_cipher *tfm;
120
121 tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
122 if (IS_ERR(tfm))
123 return NULL;
124
125 crypto_cipher_setkey(tfm, key, AES_CMAC_KEY_LEN);
126
127 return tfm;
128}
129
130
131void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
132{
133 if (tfm)
134 crypto_free_cipher(tfm);
135}
diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h
new file mode 100644
index 000000000000..0eb9a4831508
--- /dev/null
+++ b/net/mac80211/aes_cmac.h
@@ -0,0 +1,19 @@
1/*
2 * Copyright 2008, Jouni Malinen <j@w1.fi>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#ifndef AES_CMAC_H
10#define AES_CMAC_H
11
12#include <linux/crypto.h>
13
14struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]);
15void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad,
16 const u8 *data, size_t data_len, u8 *mic);
17void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm);
18
19#endif /* AES_CMAC_H */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b5f86cb17630..20af92abd61d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -43,7 +43,7 @@ struct ieee80211_local;
43 43
44/* Required encryption head and tailroom */ 44/* Required encryption head and tailroom */
45#define IEEE80211_ENCRYPT_HEADROOM 8 45#define IEEE80211_ENCRYPT_HEADROOM 8
46#define IEEE80211_ENCRYPT_TAILROOM 12 46#define IEEE80211_ENCRYPT_TAILROOM 18
47 47
48/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent 48/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent
49 * reception of at least three fragmented frames. This limit can be increased 49 * reception of at least three fragmented frames. This limit can be increased
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 425816e0996c..73ac28ca2ede 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -96,6 +96,16 @@ struct ieee80211_key {
96 u8 tx_crypto_buf[6 * AES_BLOCK_LEN]; 96 u8 tx_crypto_buf[6 * AES_BLOCK_LEN];
97 u8 rx_crypto_buf[6 * AES_BLOCK_LEN]; 97 u8 rx_crypto_buf[6 * AES_BLOCK_LEN];
98 } ccmp; 98 } ccmp;
99 struct {
100 u8 tx_pn[6];
101 u8 rx_pn[6];
102 struct crypto_cipher *tfm;
103 u32 replays; /* dot11RSNAStatsCMACReplays */
104 u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
105 /* scratch buffers for virt_to_page() (crypto API) */
106 u8 tx_crypto_buf[2 * AES_BLOCK_LEN];
107 u8 rx_crypto_buf[2 * AES_BLOCK_LEN];
108 } aes_cmac;
99 } u; 109 } u;
100 110
101 /* number of times this key has been used */ 111 /* number of times this key has been used */
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index aff46adde3f0..53e11e6ff66e 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -1,5 +1,6 @@
1/* 1/*
2 * Copyright 2002-2004, Instant802 Networks, Inc. 2 * Copyright 2002-2004, Instant802 Networks, Inc.
3 * Copyright 2008, Jouni Malinen <j@w1.fi>
3 * 4 *
4 * This program is free software; you can redistribute it and/or modify 5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as 6 * it under the terms of the GNU General Public License version 2 as
@@ -19,6 +20,7 @@
19#include "michael.h" 20#include "michael.h"
20#include "tkip.h" 21#include "tkip.h"
21#include "aes_ccm.h" 22#include "aes_ccm.h"
23#include "aes_cmac.h"
22#include "wpa.h" 24#include "wpa.h"
23 25
24ieee80211_tx_result 26ieee80211_tx_result
@@ -491,3 +493,126 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
491 493
492 return RX_CONTINUE; 494 return RX_CONTINUE;
493} 495}
496
497
498static void bip_aad(struct sk_buff *skb, u8 *aad)
499{
500 /* BIP AAD: FC(masked) || A1 || A2 || A3 */
501
502 /* FC type/subtype */
503 aad[0] = skb->data[0];
504 /* Mask FC Retry, PwrMgt, MoreData flags to zero */
505 aad[1] = skb->data[1] & ~(BIT(4) | BIT(5) | BIT(6));
506 /* A1 || A2 || A3 */
507 memcpy(aad + 2, skb->data + 4, 3 * ETH_ALEN);
508}
509
510
511static inline void bip_ipn_swap(u8 *d, const u8 *s)
512{
513 *d++ = s[5];
514 *d++ = s[4];
515 *d++ = s[3];
516 *d++ = s[2];
517 *d++ = s[1];
518 *d = s[0];
519}
520
521
522ieee80211_tx_result
523ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
524{
525 struct sk_buff *skb = tx->skb;
526 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
527 struct ieee80211_key *key = tx->key;
528 struct ieee80211_mmie *mmie;
529 u8 *pn, aad[20];
530 int i;
531
532 if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
533 /* hwaccel */
534 info->control.hw_key = &tx->key->conf;
535 return 0;
536 }
537
538 if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
539 return TX_DROP;
540
541 mmie = (struct ieee80211_mmie *) skb_put(skb, sizeof(*mmie));
542 mmie->element_id = WLAN_EID_MMIE;
543 mmie->length = sizeof(*mmie) - 2;
544 mmie->key_id = cpu_to_le16(key->conf.keyidx);
545
546 /* PN = PN + 1 */
547 pn = key->u.aes_cmac.tx_pn;
548
549 for (i = sizeof(key->u.aes_cmac.tx_pn) - 1; i >= 0; i--) {
550 pn[i]++;
551 if (pn[i])
552 break;
553 }
554 bip_ipn_swap(mmie->sequence_number, pn);
555
556 bip_aad(skb, aad);
557
558 /*
559 * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64)
560 */
561 ieee80211_aes_cmac(key->u.aes_cmac.tfm, key->u.aes_cmac.tx_crypto_buf,
562 aad, skb->data + 24, skb->len - 24, mmie->mic);
563
564 return TX_CONTINUE;
565}
566
567
568ieee80211_rx_result
569ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
570{
571 struct sk_buff *skb = rx->skb;
572 struct ieee80211_key *key = rx->key;
573 struct ieee80211_mmie *mmie;
574 u8 aad[20], mic[8], ipn[6];
575 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
576
577 if (!ieee80211_is_mgmt(hdr->frame_control))
578 return RX_CONTINUE;
579
580 if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
581 (rx->status->flag & RX_FLAG_IV_STRIPPED))
582 return RX_CONTINUE;
583
584 if (skb->len < 24 + sizeof(*mmie))
585 return RX_DROP_UNUSABLE;
586
587 mmie = (struct ieee80211_mmie *)
588 (skb->data + skb->len - sizeof(*mmie));
589 if (mmie->element_id != WLAN_EID_MMIE ||
590 mmie->length != sizeof(*mmie) - 2)
591 return RX_DROP_UNUSABLE; /* Invalid MMIE */
592
593 bip_ipn_swap(ipn, mmie->sequence_number);
594
595 if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) {
596 key->u.aes_cmac.replays++;
597 return RX_DROP_UNUSABLE;
598 }
599
600 if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
601 /* hardware didn't decrypt/verify MIC */
602 bip_aad(skb, aad);
603 ieee80211_aes_cmac(key->u.aes_cmac.tfm,
604 key->u.aes_cmac.rx_crypto_buf, aad,
605 skb->data + 24, skb->len - 24, mic);
606 if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
607 key->u.aes_cmac.icverrors++;
608 return RX_DROP_UNUSABLE;
609 }
610 }
611
612 memcpy(key->u.aes_cmac.rx_pn, ipn, 6);
613
614 /* Remove MMIE */
615 skb_trim(skb, skb->len - sizeof(*mmie));
616
617 return RX_CONTINUE;
618}
diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h
index d42d221d8a1d..baba0608313e 100644
--- a/net/mac80211/wpa.h
+++ b/net/mac80211/wpa.h
@@ -28,4 +28,9 @@ ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx);
28ieee80211_rx_result 28ieee80211_rx_result
29ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx); 29ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx);
30 30
31ieee80211_tx_result
32ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx);
33ieee80211_rx_result
34ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);
35
31#endif /* WPA_H */ 36#endif /* WPA_H */