wlan_rsn/key/exchange/handshake/fourway/
authenticator.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::fourway::{
6    self, AuthenticatorKeyReplayCounter, Config, FourwayHandshakeFrame, SupplicantKeyReplayCounter,
7};
8use crate::key::exchange::{compute_mic_from_buf, Key};
9use crate::key::gtk::Gtk;
10use crate::key::igtk::Igtk;
11use crate::key::ptk::Ptk;
12use crate::key::Tk;
13use crate::key_data::kde;
14use crate::nonce::Nonce;
15use crate::rsna::{
16    derive_key_descriptor_version, Dot11VerifiedKeyFrame, NegotiatedProtection, SecAssocUpdate,
17    UnverifiedKeyData, UpdateSink,
18};
19use crate::Error;
20use anyhow::{ensure, format_err};
21use log::{error, warn};
22use std::fmt;
23use zerocopy::SplitByteSlice;
24
25#[derive(Debug, PartialEq)]
26pub enum State {
27    Idle {
28        pmk: Vec<u8>,
29        cfg: Config,
30    },
31    AwaitingMsg2 {
32        pmk: Vec<u8>,
33        cfg: Config,
34        anonce: Nonce,
35        key_replay_counter: AuthenticatorKeyReplayCounter,
36    },
37    AwaitingMsg4 {
38        pmk: Vec<u8>,
39        ptk: Ptk,
40        gtk: Gtk,
41        igtk: Option<Igtk>,
42        cfg: Config,
43        key_replay_counter: AuthenticatorKeyReplayCounter,
44    },
45    KeysInstalled {
46        pmk: Vec<u8>,
47        ptk: Ptk,
48        gtk: Gtk,
49        igtk: Option<Igtk>,
50        cfg: Config,
51    },
52}
53
54impl fmt::Display for State {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        write!(
57            f,
58            "{}",
59            match self {
60                State::Idle { .. } => "Idle",
61                State::AwaitingMsg2 { .. } => "AwaitingMsg2",
62                State::AwaitingMsg4 { .. } => "AwaitingMsg4",
63                State::KeysInstalled { .. } => "KeysInstalled",
64            }
65        )
66    }
67}
68
69pub fn new(cfg: Config, pmk: Vec<u8>) -> State {
70    State::Idle { pmk, cfg }
71}
72
73impl State {
74    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
75    /// If [`self`] is in [`State::Idle`], then this function will push a
76    /// [`SecAssocUpdate::TxEapolKeyFrame`] into [`update_sink`] and result [`self`]. Otherwise,
77    /// [`self`] is dropped and an [`Error`] is returned.
78    pub fn initiate(
79        self,
80        update_sink: &mut UpdateSink,
81        s_key_replay_counter: SupplicantKeyReplayCounter,
82    ) -> Result<Self, Error> {
83        let key_replay_counter = AuthenticatorKeyReplayCounter::next_after(*s_key_replay_counter);
84        match self {
85            State::Idle { cfg, pmk } => {
86                let anonce = cfg.nonce_rdr.next();
87                match initiate_internal(update_sink, &cfg, key_replay_counter, &anonce[..]) {
88                    Ok(()) => Ok(State::AwaitingMsg2 { anonce, cfg, pmk, key_replay_counter }),
89                    Err(e) => Err(Error::GenericError(format!("{}", e))),
90                }
91            }
92            other_state => Err(Error::GenericError(format!(
93                "Failed to initiate Authenticator, currently in {} state",
94                other_state
95            ))),
96        }
97    }
98
99    pub fn on_eapol_key_frame<B: SplitByteSlice>(
100        self,
101        update_sink: &mut UpdateSink,
102        frame: FourwayHandshakeFrame<B>,
103    ) -> Self {
104        match self {
105            State::Idle { cfg, pmk } => {
106                error!("received EAPOL Key frame before initiate 4-Way Handshake");
107                State::Idle { cfg, pmk }
108            }
109            State::AwaitingMsg2 { pmk, cfg, anonce, key_replay_counter } => {
110                // Safe since the frame is only used for deriving the message number.
111                match frame.message_number() {
112                    fourway::MessageNumber::Message2 => {
113                        match process_message_2(
114                            update_sink,
115                            &pmk[..],
116                            &cfg,
117                            &anonce[..],
118                            key_replay_counter,
119                            frame,
120                        ) {
121                            Ok((ptk, gtk, igtk, key_replay_counter)) => {
122                                State::AwaitingMsg4 { pmk, ptk, gtk, igtk, cfg, key_replay_counter }
123                            }
124                            Err(e) => {
125                                warn!("Unable to process second EAPOL handshake key frame from supplicant: {}", e);
126                                State::AwaitingMsg2 { pmk, cfg, anonce, key_replay_counter }
127                            }
128                        }
129                    }
130                    unexpected_msg => {
131                        error!(
132                            "error: {:?}",
133                            Error::UnexpectedHandshakeMessage(unexpected_msg.into())
134                        );
135                        State::AwaitingMsg2 { pmk, cfg, anonce, key_replay_counter }
136                    }
137                }
138            }
139            State::AwaitingMsg4 { pmk, ptk, gtk, igtk, cfg, key_replay_counter } => {
140                match process_message_4(
141                    update_sink,
142                    &cfg,
143                    &ptk,
144                    &gtk,
145                    &igtk,
146                    key_replay_counter,
147                    frame,
148                ) {
149                    Ok(()) => State::KeysInstalled { pmk, ptk, gtk, igtk, cfg },
150                    Err(e) => {
151                        error!("error: {}", e);
152                        State::AwaitingMsg4 { pmk, ptk, gtk, igtk, cfg, key_replay_counter }
153                    }
154                }
155            }
156            other_state => other_state,
157        }
158    }
159
160    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
161    pub fn on_rsna_response_timeout(&self) -> Result<(), Error> {
162        match self {
163            State::AwaitingMsg2 { .. } => Err(Error::EapolHandshakeIncomplete(
164                "Client never responded to EAPOL message 1".to_string(),
165            )),
166            State::AwaitingMsg4 { .. } => Err(Error::EapolHandshakeIncomplete(
167                "Client never responded to EAPOL message 3".to_string(),
168            )),
169            _ => Ok(()),
170        }
171    }
172
173    pub fn ptk(&self) -> Option<Ptk> {
174        match self {
175            State::AwaitingMsg4 { ptk, .. } | State::KeysInstalled { ptk, .. } => Some(ptk.clone()),
176            _ => None,
177        }
178    }
179
180    pub fn gtk(&self) -> Option<Gtk> {
181        match self {
182            State::AwaitingMsg4 { gtk, .. } | State::KeysInstalled { gtk, .. } => Some(gtk.clone()),
183            _ => None,
184        }
185    }
186
187    pub fn igtk(&self) -> Option<Igtk> {
188        match self {
189            State::AwaitingMsg4 { igtk, .. } | State::KeysInstalled { igtk, .. } => igtk.clone(),
190            _ => None,
191        }
192    }
193
194    pub fn destroy(self) -> Config {
195        match self {
196            State::Idle { cfg, .. } => cfg,
197            State::AwaitingMsg2 { cfg, .. } => cfg,
198            State::AwaitingMsg4 { cfg, .. } => cfg,
199            State::KeysInstalled { cfg, .. } => cfg,
200        }
201    }
202}
203
204fn initiate_internal(
205    update_sink: &mut UpdateSink,
206    cfg: &Config,
207    key_replay_counter: AuthenticatorKeyReplayCounter,
208    anonce: &[u8],
209) -> Result<(), anyhow::Error> {
210    let protection = NegotiatedProtection::from_protection(&cfg.s_protection)?;
211    let msg1 = create_message_1(anonce, &protection, key_replay_counter)?;
212    update_sink.push(SecAssocUpdate::TxEapolKeyFrame { frame: msg1, expect_response: true });
213    Ok(())
214}
215
216fn process_message_2<B: SplitByteSlice>(
217    update_sink: &mut UpdateSink,
218    pmk: &[u8],
219    cfg: &Config,
220    anonce: &[u8],
221    key_replay_counter: AuthenticatorKeyReplayCounter,
222    frame: FourwayHandshakeFrame<B>,
223) -> Result<(Ptk, Gtk, Option<Igtk>, AuthenticatorKeyReplayCounter), anyhow::Error> {
224    let ptk = handle_message_2(&pmk[..], &cfg, &anonce[..], key_replay_counter, frame)?;
225    let gtk = cfg
226        .gtk_provider
227        .as_ref()
228        // TODO(https://fxbug.dev/42104575): Replace with Error::MissingGtkProvider
229        .ok_or_else(|| format_err!("GtkProvider is missing"))?
230        .lock()
231        .unwrap()
232        .get_gtk()
233        .clone();
234    let igtk = match fourway::get_group_mgmt_cipher(&cfg.s_protection, &cfg.a_protection)
235        .map_err(|e| format_err!("group mgmt cipher error: {:?}", e))?
236    {
237        Some(group_mgmt_cipher) => {
238            let igtk_provider = cfg
239                .igtk_provider
240                .as_ref()
241                // TODO(https://fxbug.dev/42104575): Replace with Error::MissingIgtkProvider
242                .ok_or_else(|| format_err!("IgtkProvider is missing"))?
243                .lock()
244                .unwrap();
245            let igtk_provider_cipher = igtk_provider.cipher();
246            if group_mgmt_cipher != igtk_provider_cipher {
247                // TODO(https://fxbug.dev/42104575): Replace with Error::WrongIgtkProviderCipher
248                return Err(format_err!(
249                    "wrong IgtkProvider cipher: {:?} != {:?}",
250                    group_mgmt_cipher,
251                    igtk_provider_cipher
252                ));
253            }
254            Some(igtk_provider.get_igtk())
255        }
256        None => None,
257    };
258    let protection = NegotiatedProtection::from_protection(&cfg.s_protection)?;
259    let key_replay_counter = AuthenticatorKeyReplayCounter::next_after(*key_replay_counter);
260    let msg3 = create_message_3(
261        &cfg,
262        ptk.kck(),
263        ptk.kek(),
264        &gtk,
265        &igtk,
266        &anonce[..],
267        &protection,
268        key_replay_counter,
269    )?;
270
271    update_sink.push(SecAssocUpdate::TxEapolKeyFrame { frame: msg3, expect_response: true });
272    Ok((ptk, gtk, igtk, key_replay_counter))
273}
274
275fn process_message_4<B: SplitByteSlice>(
276    update_sink: &mut UpdateSink,
277    cfg: &Config,
278    ptk: &Ptk,
279    gtk: &Gtk,
280    igtk: &Option<Igtk>,
281    key_replay_counter: AuthenticatorKeyReplayCounter,
282    frame: FourwayHandshakeFrame<B>,
283) -> Result<(), anyhow::Error> {
284    handle_message_4(cfg, ptk.kck(), key_replay_counter, frame)?;
285    update_sink.push(SecAssocUpdate::Key(Key::Ptk(ptk.clone())));
286    update_sink.push(SecAssocUpdate::Key(Key::Gtk(gtk.clone())));
287    if let Some(igtk) = igtk.as_ref() {
288        update_sink.push(SecAssocUpdate::Key(Key::Igtk(igtk.clone())));
289    }
290    Ok(())
291}
292
293// IEEE Std 802.11-2016, 12.7.6.2
294fn create_message_1<B: SplitByteSlice>(
295    anonce: B,
296    protection: &NegotiatedProtection,
297    key_replay_counter: AuthenticatorKeyReplayCounter,
298) -> Result<eapol::KeyFrameBuf, anyhow::Error> {
299    let version = derive_key_descriptor_version(eapol::KeyDescriptor::IEEE802DOT11, protection);
300    let key_info = eapol::KeyInformation(0)
301        .with_key_descriptor_version(version)
302        .with_key_type(eapol::KeyType::PAIRWISE)
303        .with_key_ack(true);
304
305    let key_len = match protection.pairwise.tk_bits() {
306        None => {
307            return Err(format_err!(
308                "unknown cipher used for pairwise key: {:?}",
309                protection.pairwise
310            ))
311        }
312        Some(tk_bits) => tk_bits / 8,
313    };
314    eapol::KeyFrameTx::new(
315        eapol::ProtocolVersion::IEEE802DOT1X2004,
316        eapol::KeyFrameFields::new(
317            eapol::KeyDescriptor::IEEE802DOT11,
318            key_info,
319            key_len,
320            *key_replay_counter,
321            eapol::to_array(&anonce),
322            [0u8; 16], // iv
323            0,         // rsc
324        ),
325        vec![],
326        protection.mic_size as usize,
327    )
328    .serialize()
329    .finalize_without_mic()
330    .map_err(|e| e.into())
331}
332
333// IEEE Std 802.11-2016, 12.7.6.3
334pub fn handle_message_2<B: SplitByteSlice>(
335    pmk: &[u8],
336    cfg: &Config,
337    anonce: &[u8],
338    key_replay_counter: AuthenticatorKeyReplayCounter,
339    frame: FourwayHandshakeFrame<B>,
340) -> Result<Ptk, anyhow::Error> {
341    // Safe: The nonce must be accessed to compute the PTK. The frame will still be fully verified
342    // before accessing any of its fields.
343    let snonce = &frame.unsafe_get_raw().key_frame_fields.key_nonce[..];
344    let protection = NegotiatedProtection::from_protection(&cfg.s_protection)?;
345
346    let ptk = Ptk::new(
347        pmk,
348        &cfg.a_addr,
349        &cfg.s_addr,
350        anonce,
351        snonce,
352        &protection.akm,
353        protection.pairwise.clone(),
354    )?;
355
356    // PTK was computed, verify the frame's MIC.
357    let frame = match frame.get() {
358        Dot11VerifiedKeyFrame::WithUnverifiedMic(unverified_mic) => {
359            match unverified_mic.verify_mic(ptk.kck(), &protection)? {
360                UnverifiedKeyData::Encrypted(_) => {
361                    return Err(format_err!("msg2 of 4-Way Handshake must not be encrypted"))
362                }
363                UnverifiedKeyData::NotEncrypted(frame) => frame,
364            }
365        }
366        Dot11VerifiedKeyFrame::WithoutMic(_) => {
367            return Err(format_err!("msg2 of 4-Way Handshake must carry a MIC"))
368        }
369    };
370    ensure!(
371        frame.key_frame_fields.key_replay_counter.to_native() == *key_replay_counter,
372        "error, expected Supplicant response to message {:?} but was {:?} in msg #2",
373        *key_replay_counter,
374        frame.key_frame_fields.key_replay_counter.to_native()
375    );
376
377    // TODO(hahnr): Key data must carry RSNE. Verify.
378
379    Ok(ptk)
380}
381
382// IEEE Std 802.11-2016, 12.7.6.4
383fn create_message_3(
384    cfg: &Config,
385    kck: &[u8],
386    kek: &[u8],
387    gtk: &Gtk,
388    igtk: &Option<Igtk>,
389    anonce: &[u8],
390    protection: &NegotiatedProtection,
391    key_replay_counter: AuthenticatorKeyReplayCounter,
392) -> Result<eapol::KeyFrameBuf, anyhow::Error> {
393    // Construct key data which contains the Beacon's RSNE and a GTK KDE.
394    let mut w = kde::Writer::new();
395    w.write_protection(&cfg.a_protection)?;
396    w.write_gtk(&kde::Gtk::new(gtk.key_id(), kde::GtkInfoTx::BothRxTx, gtk.tk()))?;
397    if let Some(igtk) = igtk.as_ref() {
398        w.write_igtk(&kde::Igtk::new(igtk.key_id, &igtk.ipn, igtk.tk()))?;
399    }
400    let key_data = w.finalize_for_encryption()?;
401    let key_iv = [0u8; 16];
402    let encrypted_key_data =
403        protection.keywrap_algorithm()?.wrap_key(kek, &key_iv, &key_data[..])?;
404
405    // Construct message.
406    let version = derive_key_descriptor_version(eapol::KeyDescriptor::IEEE802DOT11, protection);
407    let key_info = eapol::KeyInformation(0)
408        .with_key_descriptor_version(version)
409        .with_key_type(eapol::KeyType::PAIRWISE)
410        .with_key_ack(true)
411        .with_key_mic(true)
412        .with_install(true)
413        .with_secure(true)
414        .with_encrypted_key_data(true);
415
416    let key_len = match protection.pairwise.tk_bits() {
417        None => {
418            return Err(format_err!(
419                "unknown cipher used for pairwise key: {:?}",
420                protection.pairwise
421            ))
422        }
423        Some(tk_bits) => tk_bits / 8,
424    };
425
426    let msg3 = eapol::KeyFrameTx::new(
427        eapol::ProtocolVersion::IEEE802DOT1X2004,
428        eapol::KeyFrameFields::new(
429            eapol::KeyDescriptor::IEEE802DOT11,
430            key_info,
431            key_len,
432            *key_replay_counter,
433            eapol::to_array(anonce),
434            key_iv,
435            gtk.key_rsc(),
436        ),
437        encrypted_key_data,
438        protection.mic_size as usize,
439    )
440    .serialize();
441
442    let mic = compute_mic_from_buf(kck, &protection, msg3.unfinalized_buf())
443        .map_err(|e| anyhow::Error::from(e))?;
444    msg3.finalize_with_mic(&mic[..]).map_err(|e| e.into())
445}
446
447// IEEE Std 802.11-2016, 12.7.6.5
448pub fn handle_message_4<B: SplitByteSlice>(
449    cfg: &Config,
450    kck: &[u8],
451    key_replay_counter: AuthenticatorKeyReplayCounter,
452    frame: FourwayHandshakeFrame<B>,
453) -> Result<(), anyhow::Error> {
454    let protection = NegotiatedProtection::from_protection(&cfg.s_protection)?;
455    let frame = match frame.get() {
456        Dot11VerifiedKeyFrame::WithUnverifiedMic(unverified_mic) => {
457            match unverified_mic.verify_mic(kck, &protection)? {
458                UnverifiedKeyData::Encrypted(_) => {
459                    return Err(format_err!("msg4 of 4-Way Handshake must not be encrypted"))
460                }
461                UnverifiedKeyData::NotEncrypted(frame) => frame,
462            }
463        }
464        Dot11VerifiedKeyFrame::WithoutMic(_) => {
465            return Err(format_err!("msg4 of 4-Way Handshake must carry a MIC"))
466        }
467    };
468    ensure!(
469        frame.key_frame_fields.key_replay_counter.to_native() == *key_replay_counter,
470        "error, expected Supplicant response to message {:?} but was {:?} in msg #4",
471        *key_replay_counter,
472        frame.key_frame_fields.key_replay_counter.to_native()
473    );
474
475    // Note: The message's integrity was already verified by low layers.
476
477    Ok(())
478}