wlan_rsn/key/exchange/handshake/fourway/
supplicant.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use ofn 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::fourway::{self, Config, FourwayHandshakeFrame};
6use crate::key::exchange::{compute_mic_from_buf, Key};
7use crate::key::gtk::Gtk;
8use crate::key::igtk::Igtk;
9use crate::key::ptk::Ptk;
10use crate::key::Tk;
11use crate::key_data::kde;
12use crate::nonce::Nonce;
13use crate::rsna::{
14    Dot11VerifiedKeyFrame, IgtkSupport, NegotiatedProtection, ProtectionType, SecAssocUpdate,
15    UnverifiedKeyData, UpdateSink,
16};
17use crate::{key_data, Error, ProtectionInfo};
18use anyhow::{ensure, format_err};
19use eapol::KeyFrameBuf;
20use log::error;
21use zerocopy::SplitByteSlice;
22
23// IEEE Std 802.11-2016, 12.7.6.2
24fn handle_message_1<B: SplitByteSlice>(
25    cfg: &Config,
26    pmk: &[u8],
27    snonce: &[u8],
28    msg1: FourwayHandshakeFrame<B>,
29) -> Result<(KeyFrameBuf, Ptk, Nonce), anyhow::Error> {
30    let frame = match msg1.get() {
31        // Note: This is only true if PTK re-keying is not supported.
32        Dot11VerifiedKeyFrame::WithUnverifiedMic(_) => {
33            return Err(format_err!("msg1 of 4-Way Handshake cannot carry a MIC"))
34        }
35        Dot11VerifiedKeyFrame::WithoutMic(frame) => frame,
36    };
37    let anonce = frame.key_frame_fields.key_nonce;
38    let protection = NegotiatedProtection::from_protection(&cfg.s_protection)?;
39
40    let pairwise = protection.pairwise.clone();
41    let ptk =
42        Ptk::new(pmk, &cfg.a_addr, &cfg.s_addr, &anonce[..], snonce, &protection.akm, pairwise)?;
43    let msg2 = create_message_2(cfg, ptk.kck(), &protection, &frame, &snonce[..])?;
44
45    Ok((msg2, ptk, anonce))
46}
47
48// IEEE Std 802.11-2016, 12.7.6.3
49fn create_message_2<B: SplitByteSlice>(
50    cfg: &Config,
51    kck: &[u8],
52    protection: &NegotiatedProtection,
53    msg1: &eapol::KeyFrameRx<B>,
54    snonce: &[u8],
55) -> Result<KeyFrameBuf, anyhow::Error> {
56    let key_info = eapol::KeyInformation(0)
57        .with_key_descriptor_version(msg1.key_frame_fields.key_info().key_descriptor_version())
58        .with_key_type(msg1.key_frame_fields.key_info().key_type())
59        .with_key_mic(true);
60
61    let mut w = kde::Writer::new();
62    w.write_protection(&cfg.s_protection)?;
63    let key_data = w.finalize_for_plaintext()?.into();
64
65    let msg2 = eapol::KeyFrameTx::new(
66        msg1.eapol_fields.version,
67        eapol::KeyFrameFields::new(
68            msg1.key_frame_fields.descriptor_type,
69            key_info,
70            0,
71            msg1.key_frame_fields.key_replay_counter.to_native(),
72            eapol::to_array(snonce),
73            [0u8; 16], // IV
74            0,         // RSC
75        ),
76        key_data,
77        msg1.key_mic.len(),
78    )
79    .serialize();
80
81    let mic = compute_mic_from_buf(kck, &protection, msg2.unfinalized_buf())
82        .map_err(|e| anyhow::Error::from(e))?;
83    msg2.finalize_with_mic(&mic[..]).map_err(|e| e.into())
84}
85
86// IEEE Std 802.11-2016, 12.7.6.4
87// This function will never return an empty GTK unless the protection is WPA1, in which case this is
88// not set until a subsequent GroupKey handshake.
89fn handle_message_3<B: SplitByteSlice>(
90    cfg: &Config,
91    kck: &[u8],
92    kek: &[u8],
93    msg3: FourwayHandshakeFrame<B>,
94) -> Result<(KeyFrameBuf, Option<Gtk>, Option<Igtk>), anyhow::Error> {
95    let negotiated_protection = NegotiatedProtection::from_protection(&cfg.s_protection)?;
96    let (frame, key_data_elements) = match msg3.get() {
97        Dot11VerifiedKeyFrame::WithUnverifiedMic(unverified_mic) => {
98            match unverified_mic.verify_mic(kck, &negotiated_protection)? {
99                UnverifiedKeyData::Encrypted(encrypted) => {
100                    let key_data = encrypted.decrypt(kek, &negotiated_protection)?;
101                    (key_data.0, key_data::extract_elements(&key_data.1[..])?)
102                }
103                UnverifiedKeyData::NotEncrypted(keyframe) => {
104                    match negotiated_protection.protection_type {
105                        ProtectionType::LegacyWpa1 => {
106                            // WFA, WPA1 Spec. 3.1, Chapter 2.2.4
107                            // WPA1 does not encrypt the key data field during a 4-way handshake.
108                            let elements = key_data::extract_elements(&keyframe.key_data[..])?;
109                            (keyframe, elements)
110                        }
111                        _ => {
112                            return Err(format_err!(
113                                "msg3 of 4-Way Handshake must carry encrypted key data"
114                            ))
115                        }
116                    }
117                }
118            }
119        }
120        Dot11VerifiedKeyFrame::WithoutMic(_) => {
121            return Err(format_err!("msg3 of 4-Way Handshake must carry a MIC"))
122        }
123    };
124    let mut gtk: Option<key_data::kde::Gtk> = None;
125    let mut igtk: Option<Igtk> = None;
126    let mut protection: Option<ProtectionInfo> = None;
127    #[allow(clippy::collection_is_never_read)]
128    let mut _second_protection: Option<ProtectionInfo> = None;
129    for element in key_data_elements {
130        match (element, &protection) {
131            (key_data::Element::Gtk(_, e), _) => gtk = Some(e),
132            (key_data::Element::Igtk(_, e), _) => {
133                igtk = Some(Igtk::from_kde(e, negotiated_protection.group_mgmt_cipher()))
134            }
135            (key_data::Element::Rsne(e), None) => protection = Some(ProtectionInfo::Rsne(e)),
136            (key_data::Element::Rsne(e), Some(_)) => {
137                _second_protection = Some(ProtectionInfo::Rsne(e))
138            }
139            (key_data::Element::LegacyWpa1(e), None) => {
140                protection = Some(ProtectionInfo::LegacyWpa(e))
141            }
142            _ => (),
143        }
144    }
145    match (igtk.is_some(), negotiated_protection.igtk_support()) {
146        (true, IgtkSupport::Unsupported) | (false, IgtkSupport::Required) => {
147            return Err(format_err!(Error::InvalidKeyDataContent));
148        }
149        _ => (),
150    }
151
152    // Proceed if key data held a protection element matching the Authenticator's announced one.
153    let msg4 = match protection {
154        Some(protection) => {
155            ensure!(&protection == &cfg.a_protection, Error::InvalidKeyDataProtection);
156            create_message_4(&negotiated_protection, kck, &frame)?
157        }
158        None => return Err(format_err!(Error::InvalidKeyDataContent)),
159    };
160    match gtk {
161        Some(gtk) => {
162            let rsc = frame.key_frame_fields.key_rsc.to_native();
163            Ok((
164                msg4,
165                Some(Gtk::from_bytes(
166                    gtk.gtk,
167                    negotiated_protection.group_data,
168                    gtk.info.key_id(),
169                    rsc,
170                )?),
171                igtk,
172            ))
173        }
174        // In WPA1 a GTK is not specified until a subsequent GroupKey handshake.
175        None if negotiated_protection.protection_type == ProtectionType::LegacyWpa1 => {
176            Ok((msg4, None, None))
177        }
178        None => return Err(format_err!(Error::InvalidKeyDataContent)),
179    }
180}
181
182// IEEE Std 802.11-2016, 12.7.6.5
183fn create_message_4<B: SplitByteSlice>(
184    protection: &NegotiatedProtection,
185    kck: &[u8],
186    msg3: &eapol::KeyFrameRx<B>,
187) -> Result<KeyFrameBuf, anyhow::Error> {
188    // WFA, WPA1 Spec. 3.1, Chapter 2 seems to imply that the secure bit should not be set for WPA1
189    // supplicant messages, and in practice this seems to be the case.
190    let secure_bit = msg3.key_frame_fields.descriptor_type != eapol::KeyDescriptor::LEGACY_WPA1;
191    let key_info = eapol::KeyInformation(0)
192        .with_key_descriptor_version(msg3.key_frame_fields.key_info().key_descriptor_version())
193        .with_key_type(msg3.key_frame_fields.key_info().key_type())
194        .with_key_mic(true)
195        .with_secure(secure_bit);
196
197    let msg4 = eapol::KeyFrameTx::new(
198        msg3.eapol_fields.version,
199        eapol::KeyFrameFields::new(
200            msg3.key_frame_fields.descriptor_type,
201            key_info,
202            0,
203            msg3.key_frame_fields.key_replay_counter.to_native(),
204            [0u8; 32], // nonce
205            [0u8; 16], // iv
206            0,         // rsc
207        ),
208        vec![],
209        msg3.key_mic.len(),
210    )
211    .serialize();
212
213    let mic = compute_mic_from_buf(kck, &protection, msg4.unfinalized_buf())
214        .map_err(|e| anyhow::Error::from(e))?;
215    msg4.finalize_with_mic(&mic[..]).map_err(|e| e.into())
216}
217
218#[derive(Debug, PartialEq)]
219pub enum State {
220    AwaitingMsg1 { pmk: Vec<u8>, cfg: Config, snonce: Nonce },
221    AwaitingMsg3 { pmk: Vec<u8>, ptk: Ptk, snonce: Nonce, anonce: Nonce, cfg: Config },
222    KeysInstalled { pmk: Vec<u8>, ptk: Ptk, gtk: Option<Gtk>, igtk: Option<Igtk>, cfg: Config },
223}
224
225pub fn new(cfg: Config, pmk: Vec<u8>) -> State {
226    let snonce = cfg.nonce_rdr.next();
227    State::AwaitingMsg1 { pmk, cfg, snonce }
228}
229
230impl State {
231    pub fn on_eapol_key_frame<B: SplitByteSlice>(
232        self,
233        update_sink: &mut UpdateSink,
234        frame: FourwayHandshakeFrame<B>,
235    ) -> Self {
236        match self {
237            State::AwaitingMsg1 { pmk, cfg, snonce } => match frame.message_number() {
238                fourway::MessageNumber::Message1 => {
239                    match handle_message_1(&cfg, &pmk[..], &snonce[..], frame) {
240                        Err(e) => {
241                            error!("error: {}", e);
242                            // Note: No need to generate a new SNonce as the received frame is
243                            // dropped.
244                            return State::AwaitingMsg1 { pmk, cfg, snonce };
245                        }
246                        Ok((msg2, ptk, anonce)) => {
247                            update_sink.push(SecAssocUpdate::TxEapolKeyFrame {
248                                frame: msg2,
249                                expect_response: true,
250                            });
251                            State::AwaitingMsg3 { pmk, ptk, cfg, snonce, anonce }
252                        }
253                    }
254                }
255                unexpected_msg => {
256                    error!("error: {}", Error::UnexpectedHandshakeMessage(unexpected_msg.into()));
257                    // Note: No need to generate a new SNonce as the received frame is dropped.
258                    State::AwaitingMsg1 { pmk, cfg, snonce }
259                }
260            },
261            State::AwaitingMsg3 { pmk, ptk, cfg, snonce, anonce: expected_anonce, .. } => {
262                match frame.message_number() {
263                    // Restart handshake if first message was received.
264                    fourway::MessageNumber::Message1 => {
265                        // According to our understanding of IEEE 802.11-2016, 12.7.6.2 the
266                        // Authenticator and Supplicant should always generate a new nonce when
267                        // sending the first or second message of the 4-Way Handshake to its peer.
268                        // We encountered some routers in the wild which follow a different
269                        // interpretation of this chapter and are not generating a new nonce and
270                        // ignoring new nonces sent by its peer. Our security team reviewed this
271                        // behavior and decided that it's safe for the Supplicant to re-send its
272                        // SNonce and not generate a new one if the following requirements are met:
273                        // (1) The Authenticator re-used its ANonce (effectively replaying its
274                        //     original first message).
275                        // (2) No other message other than the first message of the handshake were
276                        //     exchanged and in particular, no key has been installed yet.
277                        // (3) The received message carries an increased Key Replay Counter.
278                        //
279                        // Fuchsia's ESSSA already drops message which violate (3).
280                        // (1) and (2) are verified in the Supplicant implementation:
281                        // If the third message of the handshake has ever been successfully
282                        // established the Supplicant will enter the "KeysInstalled" state which
283                        // rejects all messages but replays of the third one. Thus, (2) is met at
284                        // all times.
285                        // (1) is verified in the following check.
286                        let actual_anonce = frame.unsafe_get_raw().key_frame_fields.key_nonce;
287                        let snonce = if expected_anonce != actual_anonce {
288                            cfg.nonce_rdr.next()
289                        } else {
290                            snonce
291                        };
292                        State::AwaitingMsg1 { pmk, cfg, snonce }
293                            .on_eapol_key_frame(update_sink, frame)
294                    }
295                    // Third message of the handshake can be processed multiple times but PTK and
296                    // GTK are only installed once.
297                    fourway::MessageNumber::Message3 => {
298                        match handle_message_3(&cfg, ptk.kck(), ptk.kek(), frame) {
299                            Err(e) => {
300                                error!("error: {}", e);
301                                // Note: No need to generate a new SNonce as the received frame is
302                                // dropped.
303                                State::AwaitingMsg1 { pmk, cfg, snonce }
304                            }
305                            Ok((msg4, gtk, igtk)) => {
306                                update_sink.push(SecAssocUpdate::TxEapolKeyFrame {
307                                    frame: msg4,
308                                    expect_response: false,
309                                });
310                                update_sink.push(SecAssocUpdate::Key(Key::Ptk(ptk.clone())));
311                                if let Some(gtk) = gtk.as_ref() {
312                                    update_sink.push(SecAssocUpdate::Key(Key::Gtk(gtk.clone())))
313                                }
314                                if let Some(igtk) = igtk.as_ref() {
315                                    update_sink.push(SecAssocUpdate::Key(Key::Igtk(igtk.clone())))
316                                }
317                                State::KeysInstalled { pmk, ptk, gtk, igtk, cfg }
318                            }
319                        }
320                    }
321                    unexpected_msg => {
322                        error!(
323                            "error: {}",
324                            Error::UnexpectedHandshakeMessage(unexpected_msg.into())
325                        );
326                        // Note: No need to generate a new SNonce as the received frame is dropped.
327                        State::AwaitingMsg1 { pmk, cfg, snonce }
328                    }
329                }
330            }
331            State::KeysInstalled {
332                ref ptk,
333                gtk: ref expected_gtk,
334                igtk: ref expected_igtk,
335                ref cfg,
336                ..
337            } => {
338                match frame.message_number() {
339                    // Allow message 3 replays for robustness but never reinstall PTK, GTK or IGTK.
340                    // Reinstalling keys could create an attack surface for vulnerabilities such as
341                    // KRACK.
342                    fourway::MessageNumber::Message3 => {
343                        match handle_message_3(cfg, ptk.kck(), ptk.kek(), frame) {
344                            Err(e) => error!("error: {}", e),
345                            // Ensure GTK didn't change. IEEE 802.11-2016 isn't specifying this edge
346                            // case and leaves room for interpretation whether or not a replayed
347                            // 3rd message can carry a different GTK than originally sent.
348                            // Fuchsia decided to require all GTKs to match; if the GTK doesn't
349                            // match with the original one Fuchsia drops the received message. This
350                            // includes the case where no GTK has been set.
351                            Ok((msg4, gtk, igtk)) => {
352                                let gtk_unchanged = match (gtk, expected_gtk) {
353                                    (None, None) => true,
354                                    (Some(ref gtk_val), Some(expected_gtk_val)) => {
355                                        gtk_val.eq_tk(expected_gtk_val)
356                                    }
357                                    _ => false,
358                                };
359                                let igtk_unchanged = match (igtk, expected_igtk) {
360                                    (None, None) => true,
361                                    (Some(ref igtk_val), Some(expected_igtk_val)) => {
362                                        igtk_val.eq_tk(expected_igtk_val)
363                                    }
364                                    _ => false,
365                                };
366
367                                if gtk_unchanged && igtk_unchanged {
368                                    update_sink.push(SecAssocUpdate::TxEapolKeyFrame {
369                                        frame: msg4,
370                                        expect_response: false,
371                                    });
372                                } else {
373                                    error!("error: GTK or IGTK differs in replayed 3rd message");
374                                    // TODO(hahnr): Cancel RSNA and deauthenticate from network.
375                                    // Client won't be able to recover from this state. For now,
376                                    // Authenticator will timeout the client.
377                                }
378                            }
379                        };
380                    }
381                    unexpected_msg => {
382                        error!(
383                            "ignoring message {:?}; 4-Way Handshake already completed",
384                            unexpected_msg
385                        );
386                    }
387                };
388
389                self
390            }
391        }
392    }
393
394    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
395    pub fn on_rsna_response_timeout(&self) -> Result<(), Error> {
396        match self {
397            State::AwaitingMsg1 { .. } => Err(Error::EapolHandshakeNotStarted),
398            State::AwaitingMsg3 { .. } => Err(Error::LikelyWrongCredential),
399            State::KeysInstalled { .. } => Ok(()),
400        }
401    }
402
403    pub fn anonce(&self) -> Option<&[u8]> {
404        match self {
405            State::AwaitingMsg1 { .. } => None,
406            State::AwaitingMsg3 { anonce, .. } => Some(&anonce[..]),
407            State::KeysInstalled { .. } => None,
408        }
409    }
410
411    pub fn ptk(&self) -> Option<Ptk> {
412        match self {
413            State::AwaitingMsg3 { ptk, .. } => Some(ptk.clone()),
414            State::KeysInstalled { ptk, .. } => Some(ptk.clone()),
415            _ => None,
416        }
417    }
418
419    pub fn gtk(&self) -> Option<Gtk> {
420        match self {
421            State::KeysInstalled { gtk, .. } => gtk.clone(),
422            _ => None,
423        }
424    }
425
426    pub fn igtk(&self) -> Option<Igtk> {
427        match self {
428            State::KeysInstalled { igtk, .. } => igtk.clone(),
429            _ => None,
430        }
431    }
432
433    pub fn destroy(self) -> fourway::Config {
434        match self {
435            State::AwaitingMsg1 { cfg, .. } => cfg,
436            State::AwaitingMsg3 { cfg, .. } => cfg,
437            State::KeysInstalled { cfg, .. } => cfg,
438        }
439    }
440}