Skip to main content

wlan_rsn/key/exchange/
mod.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod handshake;
6
7use self::handshake::fourway::{self, Fourway};
8use self::handshake::group_key::{self, GroupKey};
9use crate::Error;
10use crate::key::gtk::Gtk;
11use crate::key::igtk::Igtk;
12use crate::key::ptk::Ptk;
13use crate::rsna::{Dot11VerifiedKeyFrame, NegotiatedProtection, UpdateSink};
14use zerocopy::SplitByteSlice;
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum Key {
18    Pmk(Vec<u8>),
19    Ptk(Ptk),
20    Gtk(Gtk),
21    Igtk(Igtk),
22    MicRx(Vec<u8>),
23    MicTx(Vec<u8>),
24    Smk(Vec<u8>),
25    Stk(Vec<u8>),
26}
27
28impl Key {
29    pub fn name(&self) -> &'static str {
30        match self {
31            Key::Pmk(..) => "PMK",
32            Key::Ptk(..) => "PTK",
33            Key::Gtk(..) => "GTK",
34            Key::Igtk(..) => "IGTK",
35            Key::MicRx(..) => "MIC_RX",
36            Key::MicTx(..) => "MIC_TX",
37            Key::Smk(..) => "SMK",
38            Key::Stk(..) => "STK",
39        }
40    }
41}
42
43#[derive(Debug, PartialEq)]
44pub enum Method {
45    FourWayHandshake(Box<Fourway>),
46    GroupKeyHandshake(GroupKey),
47}
48
49impl Method {
50    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
51    pub fn on_eapol_key_frame<B: SplitByteSlice>(
52        &mut self,
53        update_sink: &mut UpdateSink,
54        frame: Dot11VerifiedKeyFrame<B>,
55    ) -> Result<(), Error> {
56        match self {
57            Method::FourWayHandshake(hs) => hs.on_eapol_key_frame(update_sink, frame),
58            Method::GroupKeyHandshake(hs) => hs.on_eapol_key_frame(update_sink, frame),
59        }
60    }
61
62    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
63    pub fn on_rsna_response_timeout(&self) -> Result<(), Error> {
64        match self {
65            Method::FourWayHandshake(hs) => hs.on_rsna_response_timeout(),
66            // Only 4-Way Handshake returns critical RSNA timeout errors.
67            _ => Ok(()),
68        }
69    }
70
71    pub fn destroy(self) -> Config {
72        match self {
73            Method::FourWayHandshake(hs) => (*hs).destroy(),
74            Method::GroupKeyHandshake(hs) => hs.destroy(),
75        }
76    }
77}
78
79#[derive(Clone, Debug, PartialEq)]
80pub enum Config {
81    FourWayHandshake(fourway::Config),
82    GroupKeyHandshake(group_key::Config),
83}
84
85#[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
86/// Computes and returns a serialized key frame's MIC.
87/// Fails if the AKM has no associated integrity algorithm or MIC size.
88pub fn compute_mic_from_buf(
89    kck: &[u8],
90    protection: &NegotiatedProtection,
91    frame: &[u8],
92) -> Result<Vec<u8>, Error> {
93    let integrity_alg = protection.integrity_algorithm()?;
94    let mic_len = protection.akm.mic_bytes().ok_or(Error::UnsupportedAkmSuite)? as usize;
95    let mut mic = integrity_alg.compute(kck, frame)?;
96    mic.truncate(mic_len);
97    Ok(mic)
98}
99
100#[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
101/// Computes and returns a key frame's MIC.
102/// Fails if the AKM has no associated integrity algorithm or MIC size, the given Key Frame's MIC
103/// has a different size than the MIC length derived from the AKM or the Key Frame doesn't have its
104/// MIC bit set.
105pub fn compute_mic<B: SplitByteSlice>(
106    kck: &[u8],
107    protection: &NegotiatedProtection,
108    frame: &eapol::KeyFrameRx<B>,
109) -> Result<Vec<u8>, Error> {
110    let integrity_alg = protection.integrity_algorithm()?;
111    let mic_len = protection.akm.mic_bytes().ok_or(Error::UnsupportedAkmSuite)? as usize;
112    if !frame.key_frame_fields.key_info().key_mic() {
113        return Err(Error::ComputingMicForUnprotectedFrame);
114    }
115    if frame.key_mic.len() != mic_len {
116        return Err(Error::MicSizesDiffer(frame.key_mic.len(), mic_len));
117    }
118
119    let buf = frame.to_bytes(true);
120    let mut mic = integrity_alg.compute(kck, &buf[..])?;
121    mic.truncate(mic_len);
122    Ok(mic)
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use crate::integrity::{self, Algorithm};
129    use crate::rsna::test_util;
130    use assert_matches::assert_matches;
131    use wlan_common::ie::rsn::akm::Akm;
132
133    fn fake_key_frame(mic_len: usize) -> eapol::KeyFrameTx {
134        let key_info = eapol::KeyInformation(0).with_key_mic(true);
135        eapol::KeyFrameTx::new(
136            eapol::ProtocolVersion::IEEE802DOT1X2010,
137            eapol::KeyFrameFields::new(
138                eapol::KeyDescriptor::IEEE802DOT11,
139                key_info,
140                16,
141                0,
142                [0u8; 32],
143                [0u8; 16],
144                0,
145            ),
146            vec![],
147            mic_len,
148        )
149    }
150
151    #[test]
152    fn compute_mic_unknown_akm() {
153        const KCK: [u8; 16] = [5; 16];
154        let frame = fake_key_frame(16)
155            .serialize()
156            .finalize_with_mic(&[0u8; 16][..])
157            .expect("failed to create fake key frame");
158        let mut protection = test_util::get_rsne_protection();
159        protection.akm = Akm::new_dot11(200);
160        let result = compute_mic(&KCK[..], &protection, &frame.keyframe());
161        assert_matches!(result, Err(Error::UnknownIntegrityAlgorithm));
162    }
163
164    #[test]
165    fn compute_mic_bit_not_set() {
166        const KCK: [u8; 16] = [5; 16];
167        let mut frame = fake_key_frame(16);
168        frame.key_frame_fields.set_key_info(eapol::KeyInformation(0));
169        let frame =
170            frame.serialize().finalize_without_mic().expect("failed to create fake key frame");
171        let result = compute_mic(&KCK[..], &test_util::get_rsne_protection(), &frame.keyframe());
172        assert_matches!(result, Err(Error::ComputingMicForUnprotectedFrame));
173    }
174
175    #[test]
176    fn compute_mic_different_mic_sizes() {
177        const KCK: [u8; 16] = [5; 16];
178        let frame = fake_key_frame(0)
179            .serialize()
180            .finalize_with_mic(&[][..])
181            .expect("failed to create fake key frame");
182        let result = compute_mic(&KCK[..], &test_util::get_rsne_protection(), &frame.keyframe());
183        assert_matches!(result, Err(Error::MicSizesDiffer(0, 16)));
184    }
185
186    #[test]
187    fn compute_mic_success() {
188        const KCK: [u8; 16] = [5; 16];
189        let frame = fake_key_frame(16)
190            .serialize()
191            .finalize_with_mic(&[0u8; 16][..])
192            .expect("failed to create fake key frame");
193        let mic = compute_mic(&KCK[..], &test_util::get_rsne_protection(), &frame.keyframe())
194            .expect("expected failure with unsupported AKM");
195        assert!(integrity::hmac_sha1::HmacSha1::new().verify(&KCK[..], &frame[..], &mic[..]));
196    }
197}