wlan_rsn/key/exchange/handshake/group_key/
supplicant.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
5use crate::key::exchange::handshake::group_key::{self, Config, GroupKeyHandshakeFrame};
6use crate::key::exchange::{Key, compute_mic_from_buf};
7use crate::key::gtk::Gtk;
8use crate::key::igtk::Igtk;
9use crate::key_data::kde::GtkInfoTx;
10use crate::rsna::{
11    Dot11VerifiedKeyFrame, IgtkSupport, ProtectionType, SecAssocUpdate, UnverifiedKeyData,
12    UpdateSink,
13};
14use crate::{Error, format_rsn_err, key_data};
15use bytes::Bytes;
16use eapol::KeyFrameBuf;
17use zerocopy::SplitByteSlice;
18
19/// Implementation of 802.11's Group Key Handshake for the Supplicant role.
20#[derive(Debug, PartialEq)]
21pub struct Supplicant {
22    pub cfg: group_key::Config,
23    pub kck: Bytes,
24    pub kek: Bytes,
25}
26
27impl Supplicant {
28    // IEEE Std 802.11-2016, 12.7.7.2
29    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
30    pub fn on_eapol_key_frame<B: SplitByteSlice>(
31        &mut self,
32        update_sink: &mut UpdateSink,
33        msg1: GroupKeyHandshakeFrame<B>,
34    ) -> Result<(), Error> {
35        let (frame, key_data) = match msg1.get() {
36            Dot11VerifiedKeyFrame::WithUnverifiedMic(unverified_mic) => {
37                match unverified_mic.verify_mic(&self.kck[..], &self.cfg.protection)? {
38                    UnverifiedKeyData::Encrypted(encrypted) => {
39                        encrypted.decrypt(&self.kek[..], &self.cfg.protection)?
40                    }
41                    UnverifiedKeyData::NotEncrypted(keyframe) => {
42                        match self.cfg.protection.protection_type {
43                            ProtectionType::LegacyWpa1 => {
44                                // WFA, WPA1 Spec. 3.1, Chapter 2.2.4
45                                // WPA1 GTK is encrypted but does not set the encrypted bit, so we
46                                // handle it as a special case.
47                                let algorithm = &self.cfg.protection.keywrap_algorithm()?;
48                                let key_data = algorithm.unwrap_key(
49                                    &self.kek[..],
50                                    &keyframe.key_frame_fields.key_iv,
51                                    &keyframe.key_data[..],
52                                )?;
53                                (keyframe, key_data)
54                            }
55                            _ => {
56                                return Err(format_rsn_err!(
57                                    "msg1 of Group-Key Handshake must carry encrypted key data"
58                                ));
59                            }
60                        }
61                    }
62                }
63            }
64            Dot11VerifiedKeyFrame::WithoutMic(_) => {
65                return Err(format_rsn_err!("msg1 of Group-Key Handshake must carry a MIC"));
66            }
67        };
68
69        // Extract GTK from data.
70        let (gtk, igtk) = self.extract_key_data(&frame, &key_data[..])?;
71
72        match (igtk.is_some(), self.cfg.protection.igtk_support()) {
73            (true, IgtkSupport::Unsupported) | (false, IgtkSupport::Required) => {
74                return Err(Error::InvalidKeyDataContent);
75            }
76            _ => (),
77        }
78
79        // Construct second message of handshake.
80        let msg2 = self.create_message_2(&frame)?;
81        update_sink.push(SecAssocUpdate::TxEapolKeyFrame { frame: msg2, expect_response: false });
82        update_sink.push(SecAssocUpdate::Key(Key::Gtk(gtk)));
83        if let Some(igtk) = igtk {
84            update_sink.push(SecAssocUpdate::Key(Key::Igtk(igtk)));
85        }
86
87        Ok(())
88    }
89
90    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
91    fn extract_key_data<B: SplitByteSlice>(
92        &self,
93        frame: &eapol::KeyFrameRx<B>,
94        key_data: &[u8],
95    ) -> Result<(Gtk, Option<Igtk>), Error> {
96        let (gtk, igtk) = match self.cfg.protection.protection_type {
97            ProtectionType::LegacyWpa1 => {
98                let key_id = frame.key_frame_fields.key_info().legacy_wpa1_key_id() as u8;
99                (key_data::kde::Gtk::new(key_id, GtkInfoTx::BothRxTx, key_data), None)
100            }
101            _ => {
102                let mut gtk = None;
103                let mut igtk = None;
104                for element in key_data::extract_elements(key_data)? {
105                    match element {
106                        key_data::Element::Gtk(_, e) => gtk = Some(e),
107                        key_data::Element::Igtk(_, e) => igtk = Some(e),
108                        _ => (),
109                    }
110                }
111                (
112                    gtk.ok_or(format_rsn_err!(
113                        "GTK KDE not present in key data of Group-Key Handshakes's 1st message"
114                    ))?,
115                    igtk,
116                )
117            }
118        };
119
120        let key_rsc = frame.key_frame_fields.key_rsc.get();
121        Ok((
122            Gtk::from_bytes(
123                gtk.gtk,
124                self.cfg.protection.group_data.clone(),
125                gtk.info.key_id(),
126                key_rsc,
127            )?,
128            igtk.map(|element| Igtk::from_kde(element, self.cfg.protection.group_mgmt_cipher())),
129        ))
130    }
131
132    // IEEE Std 802.11-2016, 12.7.7.3
133    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
134    fn create_message_2<B: SplitByteSlice>(
135        &self,
136        msg1: &eapol::KeyFrameRx<B>,
137    ) -> Result<KeyFrameBuf, Error> {
138        let mut key_info = eapol::KeyInformation(0)
139            .with_key_descriptor_version(msg1.key_frame_fields.key_info().key_descriptor_version())
140            .with_key_type(msg1.key_frame_fields.key_info().key_type())
141            .with_key_mic(true)
142            .with_secure(true);
143
144        if msg1.key_frame_fields.descriptor_type == eapol::KeyDescriptor::LEGACY_WPA1 {
145            key_info = key_info
146                .with_legacy_wpa1_key_id(msg1.key_frame_fields.key_info().legacy_wpa1_key_id());
147        }
148
149        let msg2 = eapol::KeyFrameTx::new(
150            msg1.eapol_fields.version,
151            eapol::KeyFrameFields::new(
152                msg1.key_frame_fields.descriptor_type,
153                key_info,
154                0,
155                msg1.key_frame_fields.key_replay_counter.get(),
156                [0u8; 32], // nonce
157                [0u8; 16], // IV
158                0,         // RSC
159            ),
160            vec![],
161            self.cfg.protection.akm.mic_bytes().ok_or(Error::UnsupportedAkmSuite)? as usize,
162        )
163        .serialize();
164        let mic =
165            compute_mic_from_buf(&self.kck[..], &self.cfg.protection, msg2.unfinalized_buf())?;
166        msg2.finalize_with_mic(&mic[..]).map_err(|e| e.into())
167    }
168
169    pub fn destroy(self) -> Config {
170        self.cfg
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    use crate::key::exchange::handshake::group_key::GroupKey;
178    use crate::key_data::kde;
179    use crate::rsna::{NegotiatedProtection, Role, test_util};
180    use std::sync::LazyLock;
181    use wlan_common::ie::rsn::cipher::{CIPHER_BIP_CMAC_128, CIPHER_CCMP_128, Cipher, TKIP};
182    use wlan_common::organization::Oui;
183    use zerocopy::byteorder::big_endian::U64 as BigEndianU64;
184
185    static GTK: LazyLock<Box<[u8]>> = LazyLock::new(|| vec![3; 16].into_boxed_slice());
186    static WPA1_GTK: LazyLock<Box<[u8]>> = LazyLock::new(|| vec![3; 32].into_boxed_slice());
187
188    const KCK: [u8; 16] = [1; 16];
189    const KEK: [u8; 16] = [2; 16];
190    const IGTK: [u8; 16] = [4; 16];
191    const GTK_RSC: u64 = 81234;
192    const GTK_KEY_ID: u8 = 2;
193    const IGTK_IPN: [u8; 6] = [0xab; 6];
194    const IGTK_KEY_ID: u16 = 4;
195
196    fn make_verified<B: SplitByteSlice>(
197        key_frame: eapol::KeyFrameRx<B>,
198        role: Role,
199        protection: &NegotiatedProtection,
200    ) -> Result<Dot11VerifiedKeyFrame<B>, Error> {
201        Dot11VerifiedKeyFrame::from_frame(key_frame, &role, protection, 0)
202    }
203
204    enum Msg1Config {
205        Wpa2,
206        Wpa3,
207    }
208
209    fn fake_msg1(protection_type: Msg1Config) -> eapol::KeyFrameBuf {
210        let mut w = kde::Writer::new();
211        w.write_gtk(&kde::Gtk::new(GTK_KEY_ID, kde::GtkInfoTx::BothRxTx, &GTK[..]))
212            .expect("error writing GTK KDE");
213        if let Msg1Config::Wpa3 = &protection_type {
214            w.write_igtk(&kde::Igtk::new(IGTK_KEY_ID, &IGTK_IPN[..], &IGTK[..]))
215                .expect("error writing IGTK KDE");
216        }
217        let key_data = w.finalize_for_encryption().expect("error finalizing key data");
218        let encrypted_key_data =
219            test_util::encrypt_key_data(&KEK[..], &test_util::get_rsne_protection(), &key_data[..]);
220
221        let mut key_info = eapol::KeyInformation::default();
222        key_info.set_key_ack(true);
223        key_info.set_key_mic(true);
224        key_info.set_secure(true);
225        key_info.set_encrypted_key_data(true);
226        key_info.set_key_descriptor_version(match &protection_type {
227            Msg1Config::Wpa2 => 2,
228            Msg1Config::Wpa3 => 0,
229        });
230        let mut key_frame_fields: eapol::KeyFrameFields = Default::default();
231        key_frame_fields.set_key_info(key_info);
232        key_frame_fields.key_rsc = BigEndianU64::new(GTK_RSC);
233        key_frame_fields.descriptor_type = eapol::KeyDescriptor::IEEE802DOT11;
234        let key_frame = eapol::KeyFrameTx::new(
235            eapol::ProtocolVersion::IEEE802DOT1X2004,
236            key_frame_fields,
237            encrypted_key_data,
238            16,
239        )
240        .serialize();
241        let protection = match &protection_type {
242            Msg1Config::Wpa2 => test_util::get_rsne_protection(),
243            Msg1Config::Wpa3 => test_util::get_wpa3_protection(),
244        };
245        let mic = compute_mic_from_buf(&KCK[..], &protection, key_frame.unfinalized_buf())
246            .expect("error updating MIC");
247        key_frame.finalize_with_mic(&mic[..]).expect("error finalizing keyframe")
248    }
249
250    fn fake_gtk() -> Gtk {
251        Gtk::from_bytes(GTK.clone(), CIPHER_CCMP_128, GTK_KEY_ID, GTK_RSC)
252            .expect("error creating expected GTK")
253    }
254
255    fn fake_igtk() -> Igtk {
256        Igtk {
257            igtk: IGTK.to_vec(),
258            ipn: IGTK_IPN,
259            key_id: IGTK_KEY_ID,
260            cipher: CIPHER_BIP_CMAC_128,
261        }
262    }
263
264    fn fake_msg1_wpa1_deprecated() -> eapol::KeyFrameBuf {
265        let encrypted_key_data =
266            test_util::encrypt_key_data(&KEK[..], &test_util::get_wpa1_protection(), &WPA1_GTK[..]);
267
268        let mut key_frame_fields: eapol::KeyFrameFields = Default::default();
269        key_frame_fields.set_key_info(
270            eapol::KeyInformation::default()
271                .with_key_descriptor_version(1)
272                .with_legacy_wpa1_key_id(GTK_KEY_ID as u16)
273                .with_key_ack(true)
274                .with_key_mic(true)
275                .with_secure(true),
276        );
277        key_frame_fields.key_rsc = BigEndianU64::new(GTK_RSC);
278        key_frame_fields.descriptor_type = eapol::KeyDescriptor::LEGACY_WPA1;
279        let key_frame = eapol::KeyFrameTx::new(
280            eapol::ProtocolVersion::IEEE802DOT1X2001,
281            key_frame_fields,
282            encrypted_key_data,
283            16,
284        )
285        .serialize();
286        let mic = compute_mic_from_buf(
287            &KCK[..],
288            &test_util::get_wpa1_protection(),
289            key_frame.unfinalized_buf(),
290        )
291        .expect("error updating MIC");
292        key_frame.finalize_with_mic(&mic[..]).expect("error finalizing keyframe")
293    }
294
295    fn fake_gtk_wpa1() -> Gtk {
296        Gtk::from_bytes(
297            WPA1_GTK.clone(),
298            Cipher { oui: Oui::MSFT, suite_type: TKIP },
299            GTK_KEY_ID,
300            GTK_RSC,
301        )
302        .expect("error creating expected GTK")
303    }
304
305    #[test]
306    fn full_supplicant_test() {
307        let protection = test_util::get_rsne_protection();
308        let mut handshake = GroupKey::new(
309            Config { role: Role::Supplicant, protection: protection.clone() },
310            &KCK[..],
311            &KEK[..],
312        )
313        .expect("error creating Group Key Handshake");
314
315        // Let Supplicant handle key frame.
316        let mut update_sink = UpdateSink::default();
317        let msg1 = fake_msg1(Msg1Config::Wpa2);
318        let keyframe = msg1.keyframe();
319        let msg1_verified = make_verified(keyframe, Role::Supplicant, &protection)
320            .expect("error verifying group frame");
321        handshake
322            .on_eapol_key_frame(&mut update_sink, msg1_verified)
323            .expect("error processing msg1 of Group Key Handshake");
324
325        // Verify correct GTK was derived.
326        let actual_gtk = test_util::expect_reported_gtk(&update_sink);
327        let expected_gtk = fake_gtk();
328        assert_eq!(actual_gtk, expected_gtk);
329    }
330
331    #[test]
332    fn full_wpa3_supplicant_test() {
333        let protection = test_util::get_wpa3_protection();
334        let mut handshake = GroupKey::new(
335            Config { role: Role::Supplicant, protection: protection.clone() },
336            &KCK[..],
337            &KEK[..],
338        )
339        .expect("error creating Group Key Handshake");
340
341        // Let Supplicant handle key frame.
342        let mut update_sink = UpdateSink::default();
343        let msg1 = fake_msg1(Msg1Config::Wpa3);
344        let keyframe = msg1.keyframe();
345        let msg1_verified = make_verified(keyframe, Role::Supplicant, &protection)
346            .expect("error verifying group frame");
347        handshake
348            .on_eapol_key_frame(&mut update_sink, msg1_verified)
349            .expect("error processing msg1 of Group Key Handshake");
350
351        // Verify correct GTK was derived.
352        let actual_gtk = test_util::expect_reported_gtk(&update_sink);
353        let expected_gtk = fake_gtk();
354        assert_eq!(actual_gtk, expected_gtk);
355        let actual_igtk = test_util::expect_reported_igtk(&update_sink);
356        let expected_igtk = fake_igtk();
357        assert_eq!(actual_igtk, expected_igtk);
358    }
359
360    #[test]
361    fn full_wpa1_supplicant_test() {
362        let protection = test_util::get_wpa1_protection();
363        let mut handshake = GroupKey::new(
364            Config { role: Role::Supplicant, protection: protection.clone() },
365            &KCK[..],
366            &KEK[..],
367        )
368        .expect("error creating Group Key Handshake");
369
370        // Let Supplicant handle key frame.
371        let mut update_sink = UpdateSink::default();
372        let msg1 = fake_msg1_wpa1_deprecated();
373        let keyframe = msg1.keyframe();
374        let msg1_verified = make_verified(keyframe, Role::Supplicant, &protection)
375            .expect("error verifying group frame");
376        handshake
377            .on_eapol_key_frame(&mut update_sink, msg1_verified)
378            .expect("error processing msg1 of Group Key Handshake");
379
380        // Verify correct GTK was derived.
381        let actual_gtk = test_util::expect_reported_gtk(&update_sink);
382        let expected_gtk = fake_gtk_wpa1();
383        assert_eq!(actual_gtk, expected_gtk);
384    }
385}