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::{compute_mic_from_buf, Key};
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::{format_rsn_err, key_data, Error};
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.to_native();
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.to_native(),
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::{test_util, NegotiatedProtection, Role};
180    use lazy_static::lazy_static;
181    use wlan_common::big_endian::BigEndianU64;
182    use wlan_common::ie::rsn::cipher::{Cipher, CIPHER_BIP_CMAC_128, CIPHER_CCMP_128, TKIP};
183    use wlan_common::organization::Oui;
184
185    lazy_static! {
186        static ref GTK: Box<[u8]> = vec![3; 16].into_boxed_slice();
187        static ref WPA1_GTK: Box<[u8]> = vec![3; 32].into_boxed_slice();
188    }
189
190    const KCK: [u8; 16] = [1; 16];
191    const KEK: [u8; 16] = [2; 16];
192    const IGTK: [u8; 16] = [4; 16];
193    const GTK_RSC: u64 = 81234;
194    const GTK_KEY_ID: u8 = 2;
195    const IGTK_IPN: [u8; 6] = [0xab; 6];
196    const IGTK_KEY_ID: u16 = 4;
197
198    fn make_verified<B: SplitByteSlice>(
199        key_frame: eapol::KeyFrameRx<B>,
200        role: Role,
201        protection: &NegotiatedProtection,
202    ) -> Result<Dot11VerifiedKeyFrame<B>, Error> {
203        Dot11VerifiedKeyFrame::from_frame(key_frame, &role, protection, 0)
204    }
205
206    enum Msg1Config {
207        Wpa2,
208        Wpa3,
209    }
210
211    fn fake_msg1(protection_type: Msg1Config) -> eapol::KeyFrameBuf {
212        let mut w = kde::Writer::new();
213        w.write_gtk(&kde::Gtk::new(GTK_KEY_ID, kde::GtkInfoTx::BothRxTx, &GTK[..]))
214            .expect("error writing GTK KDE");
215        if let Msg1Config::Wpa3 = &protection_type {
216            w.write_igtk(&kde::Igtk::new(IGTK_KEY_ID, &IGTK_IPN[..], &IGTK[..]))
217                .expect("error writing IGTK KDE");
218        }
219        let key_data = w.finalize_for_encryption().expect("error finalizing key data");
220        let encrypted_key_data =
221            test_util::encrypt_key_data(&KEK[..], &test_util::get_rsne_protection(), &key_data[..]);
222
223        let mut key_info = eapol::KeyInformation::default();
224        key_info.set_key_ack(true);
225        key_info.set_key_mic(true);
226        key_info.set_secure(true);
227        key_info.set_encrypted_key_data(true);
228        key_info.set_key_descriptor_version(match &protection_type {
229            Msg1Config::Wpa2 => 2,
230            Msg1Config::Wpa3 => 0,
231        });
232        let mut key_frame_fields: eapol::KeyFrameFields = Default::default();
233        key_frame_fields.set_key_info(key_info);
234        key_frame_fields.key_rsc = BigEndianU64::from_native(GTK_RSC);
235        key_frame_fields.descriptor_type = eapol::KeyDescriptor::IEEE802DOT11;
236        let key_frame = eapol::KeyFrameTx::new(
237            eapol::ProtocolVersion::IEEE802DOT1X2004,
238            key_frame_fields,
239            encrypted_key_data,
240            16,
241        )
242        .serialize();
243        let protection = match &protection_type {
244            Msg1Config::Wpa2 => test_util::get_rsne_protection(),
245            Msg1Config::Wpa3 => test_util::get_wpa3_protection(),
246        };
247        let mic = compute_mic_from_buf(&KCK[..], &protection, key_frame.unfinalized_buf())
248            .expect("error updating MIC");
249        key_frame.finalize_with_mic(&mic[..]).expect("error finalizing keyframe")
250    }
251
252    fn fake_gtk() -> Gtk {
253        Gtk::from_bytes(GTK.clone(), CIPHER_CCMP_128, GTK_KEY_ID, GTK_RSC)
254            .expect("error creating expected GTK")
255    }
256
257    fn fake_igtk() -> Igtk {
258        Igtk {
259            igtk: IGTK.to_vec(),
260            ipn: IGTK_IPN,
261            key_id: IGTK_KEY_ID,
262            cipher: CIPHER_BIP_CMAC_128,
263        }
264    }
265
266    fn fake_msg1_wpa1_deprecated() -> eapol::KeyFrameBuf {
267        let encrypted_key_data =
268            test_util::encrypt_key_data(&KEK[..], &test_util::get_wpa1_protection(), &WPA1_GTK[..]);
269
270        let mut key_frame_fields: eapol::KeyFrameFields = Default::default();
271        key_frame_fields.set_key_info(
272            eapol::KeyInformation::default()
273                .with_key_descriptor_version(1)
274                .with_legacy_wpa1_key_id(GTK_KEY_ID as u16)
275                .with_key_ack(true)
276                .with_key_mic(true)
277                .with_secure(true),
278        );
279        key_frame_fields.key_rsc = BigEndianU64::from_native(GTK_RSC);
280        key_frame_fields.descriptor_type = eapol::KeyDescriptor::LEGACY_WPA1;
281        let key_frame = eapol::KeyFrameTx::new(
282            eapol::ProtocolVersion::IEEE802DOT1X2001,
283            key_frame_fields,
284            encrypted_key_data,
285            16,
286        )
287        .serialize();
288        let mic = compute_mic_from_buf(
289            &KCK[..],
290            &test_util::get_wpa1_protection(),
291            key_frame.unfinalized_buf(),
292        )
293        .expect("error updating MIC");
294        key_frame.finalize_with_mic(&mic[..]).expect("error finalizing keyframe")
295    }
296
297    fn fake_gtk_wpa1() -> Gtk {
298        Gtk::from_bytes(
299            WPA1_GTK.clone(),
300            Cipher { oui: Oui::MSFT, suite_type: TKIP },
301            GTK_KEY_ID,
302            GTK_RSC,
303        )
304        .expect("error creating expected GTK")
305    }
306
307    #[test]
308    fn full_supplicant_test() {
309        let protection = test_util::get_rsne_protection();
310        let mut handshake = GroupKey::new(
311            Config { role: Role::Supplicant, protection: protection.clone() },
312            &KCK[..],
313            &KEK[..],
314        )
315        .expect("error creating Group Key Handshake");
316
317        // Let Supplicant handle key frame.
318        let mut update_sink = UpdateSink::default();
319        let msg1 = fake_msg1(Msg1Config::Wpa2);
320        let keyframe = msg1.keyframe();
321        let msg1_verified = make_verified(keyframe, Role::Supplicant, &protection)
322            .expect("error verifying group frame");
323        handshake
324            .on_eapol_key_frame(&mut update_sink, msg1_verified)
325            .expect("error processing msg1 of Group Key Handshake");
326
327        // Verify correct GTK was derived.
328        let actual_gtk = test_util::expect_reported_gtk(&update_sink);
329        let expected_gtk = fake_gtk();
330        assert_eq!(actual_gtk, expected_gtk);
331    }
332
333    #[test]
334    fn full_wpa3_supplicant_test() {
335        let protection = test_util::get_wpa3_protection();
336        let mut handshake = GroupKey::new(
337            Config { role: Role::Supplicant, protection: protection.clone() },
338            &KCK[..],
339            &KEK[..],
340        )
341        .expect("error creating Group Key Handshake");
342
343        // Let Supplicant handle key frame.
344        let mut update_sink = UpdateSink::default();
345        let msg1 = fake_msg1(Msg1Config::Wpa3);
346        let keyframe = msg1.keyframe();
347        let msg1_verified = make_verified(keyframe, Role::Supplicant, &protection)
348            .expect("error verifying group frame");
349        handshake
350            .on_eapol_key_frame(&mut update_sink, msg1_verified)
351            .expect("error processing msg1 of Group Key Handshake");
352
353        // Verify correct GTK was derived.
354        let actual_gtk = test_util::expect_reported_gtk(&update_sink);
355        let expected_gtk = fake_gtk();
356        assert_eq!(actual_gtk, expected_gtk);
357        let actual_igtk = test_util::expect_reported_igtk(&update_sink);
358        let expected_igtk = fake_igtk();
359        assert_eq!(actual_igtk, expected_igtk);
360    }
361
362    #[test]
363    fn full_wpa1_supplicant_test() {
364        let protection = test_util::get_wpa1_protection();
365        let mut handshake = GroupKey::new(
366            Config { role: Role::Supplicant, protection: protection.clone() },
367            &KCK[..],
368            &KEK[..],
369        )
370        .expect("error creating Group Key Handshake");
371
372        // Let Supplicant handle key frame.
373        let mut update_sink = UpdateSink::default();
374        let msg1 = fake_msg1_wpa1_deprecated();
375        let keyframe = msg1.keyframe();
376        let msg1_verified = make_verified(keyframe, Role::Supplicant, &protection)
377            .expect("error verifying group frame");
378        handshake
379            .on_eapol_key_frame(&mut update_sink, msg1_verified)
380            .expect("error processing msg1 of Group Key Handshake");
381
382        // Verify correct GTK was derived.
383        let actual_gtk = test_util::expect_reported_gtk(&update_sink);
384        let expected_gtk = fake_gtk_wpa1();
385        assert_eq!(actual_gtk, expected_gtk);
386    }
387}