diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Makefile | 1 | ||||
-rw-r--r-- | net/mac80211/aes_cmac.c | 135 | ||||
-rw-r--r-- | net/mac80211/aes_cmac.h | 19 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
-rw-r--r-- | net/mac80211/key.h | 10 | ||||
-rw-r--r-- | net/mac80211/wpa.c | 125 | ||||
-rw-r--r-- | net/mac80211/wpa.h | 5 |
7 files changed, 296 insertions, 1 deletions
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 | |||
25 | static 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 | |||
38 | static 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 | |||
98 | void 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 | |||
117 | struct 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 | |||
131 | void 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 | |||
14 | struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]); | ||
15 | void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, | ||
16 | const u8 *data, size_t data_len, u8 *mic); | ||
17 | void 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 | ||
24 | ieee80211_tx_result | 26 | ieee80211_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 | |||
498 | static 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 | |||
511 | static 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 | |||
522 | ieee80211_tx_result | ||
523 | ieee80211_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 | |||
568 | ieee80211_rx_result | ||
569 | ieee80211_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); | |||
28 | ieee80211_rx_result | 28 | ieee80211_rx_result |
29 | ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx); | 29 | ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx); |
30 | 30 | ||
31 | ieee80211_tx_result | ||
32 | ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx); | ||
33 | ieee80211_rx_result | ||
34 | ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx); | ||
35 | |||
31 | #endif /* WPA_H */ | 36 | #endif /* WPA_H */ |