wlan_rsn/key/exchange/handshake/group_key/
supplicant.rs1use 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#[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 #[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 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 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 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 #[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], [0u8; 16], 0, ),
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, >K[..]))
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 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 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 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 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 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 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}