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