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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use super::suite_selector;
use crate::ie::rsn::suite_selector::OUI;
use crate::organization::Oui;
use std::fmt;

macro_rules! return_none_if_unknown_usage {
    ($e:expr) => {
        if !$e.has_known_usage() {
            return None;
        }
    };
}

// IEEE Std 802.11-2016, 9.4.2.25.2, Table 9-131
pub const GROUP_CIPHER_SUITE: u8 = 0;
pub const WEP_40: u8 = 1;
pub const TKIP: u8 = 2;
// 3 - Reserved.
pub const CCMP_128: u8 = 4;
pub const WEP_104: u8 = 5;
pub const BIP_CMAC_128: u8 = 6;
pub const GROUP_ADDRESSED_TRAFFIC_NOT_ALLOWED: u8 = 7;
pub const GCMP_128: u8 = 8;
pub const GCMP_256: u8 = 9;
pub const CCMP_256: u8 = 10;
pub const BIP_GMAC_128: u8 = 11;
pub const BIP_GMAC_256: u8 = 12;
pub const BIP_CMAC_256: u8 = 13;
// 14-255 - Reserved.

// Shorthands for the most commonly constructed ciphers
pub const CIPHER_TKIP: Cipher = Cipher::new_dot11(TKIP);
pub const CIPHER_CCMP_128: Cipher = Cipher::new_dot11(CCMP_128);
pub const CIPHER_BIP_CMAC_128: Cipher = Cipher::new_dot11(BIP_CMAC_128);
pub const CIPHER_GCMP_256: Cipher = Cipher::new_dot11(GCMP_256);
pub const CIPHER_BIP_CMAC_256: Cipher = Cipher::new_dot11(BIP_CMAC_256);

#[derive(PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
pub struct Cipher {
    pub oui: Oui,
    pub suite_type: u8,
}

impl Cipher {
    /// Creates a new AKM instance for 802.11 specified AKMs.
    /// See IEEE Std 802.11-2016, 9.4.2.25.2, Table 9-131
    pub const fn new_dot11(suite_type: u8) -> Self {
        Cipher { oui: OUI, suite_type }
    }

    /// Reserved and vendor specific cipher suites have no known usage and require special
    /// treatments.
    pub fn has_known_usage(&self) -> bool {
        if self.is_vendor_specific() {
            // Support MSFT TKIP/CCMP for WPA1
            self.oui == Oui::MSFT && (self.suite_type == TKIP || self.suite_type == CCMP_128)
        } else {
            !self.is_reserved()
        }
    }

    pub fn is_vendor_specific(&self) -> bool {
        // IEEE 802.11-2016, 9.4.2.25.2, Table 9-131
        !self.oui.eq(&OUI)
    }

    pub fn is_reserved(&self) -> bool {
        // IEEE 802.11-2016, 9.4.2.25.2, Table 9-131
        (self.suite_type == 3 || self.suite_type >= 14) && !self.is_vendor_specific()
    }

    pub fn is_enhanced(&self) -> bool {
        // IEEE Std 802.11-2016, 4.3.8
        if !self.has_known_usage() {
            false
        } else {
            match self.suite_type {
                GROUP_CIPHER_SUITE | WEP_40 | WEP_104 | GROUP_ADDRESSED_TRAFFIC_NOT_ALLOWED => {
                    false
                }
                _ => true,
            }
        }
    }

    pub fn supports_gtk(&self) -> Option<bool> {
        return_none_if_unknown_usage!(self);

        // IEEE 802.11-2016, 9.4.2.25.2, Table 9-132
        match self.suite_type {
            1..=5 | 8..=10 => Some(true),
            0 | 6 | 11..=13 => Some(false),
            _ => None,
        }
    }

    pub fn supports_ptk(&self) -> Option<bool> {
        return_none_if_unknown_usage!(self);

        // IEEE 802.11-2016, 9.4.2.25.2, Table 9-132
        match self.suite_type {
            0 | 2..=4 | 8..=10 => Some(true),
            1 | 5 | 6 | 11..=13 => Some(false),
            _ => None,
        }
    }

    pub fn supports_igtk(&self) -> Option<bool> {
        return_none_if_unknown_usage!(self);

        // IEEE 802.11-2016, 9.4.2.25.2, Table 9-132
        match self.suite_type {
            6 | 11..=13 => Some(true),
            0 | 1..=5 | 8..=10 => Some(false),
            _ => None,
        }
    }

    pub fn tk_bytes(&self) -> Option<u8> {
        return_none_if_unknown_usage!(self);

        // IEEE 802.11-2016, 12.7.2, Table 12-4
        match self.suite_type {
            1 => Some(5),
            5 => Some(13),
            4 | 6 | 8 | 11 => Some(16),
            2 | 9 | 10 | 12 | 13 => Some(32),
            _ => None,
        }
    }

    pub fn tk_bits(&self) -> Option<u16> {
        self.tk_bytes().map(|x| 8 * u16::from(x))
    }
}

impl suite_selector::Factory for Cipher {
    type Suite = Cipher;

    fn new(oui: Oui, suite_type: u8) -> Self::Suite {
        Cipher { oui, suite_type }
    }
}

impl fmt::Debug for Cipher {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:02X}-{:02X}-{:02X}:{}", self.oui[0], self.oui[1], self.oui[2], self.suite_type)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_new_dot11() {
        let ccmp = CIPHER_CCMP_128;
        assert!(!ccmp.is_vendor_specific());
        assert!(ccmp.has_known_usage());
        assert!(ccmp.is_enhanced());
        assert!(!ccmp.is_reserved());
    }
}