aboutsummaryrefslogtreecommitdiff
path: root/cyw43/src/control.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-09-09 01:09:15 +0200
committerDario Nieuwenhuis <[email protected]>2024-09-09 02:13:25 +0200
commitb9a1aaea5b89bd5689796bdfa4227353ee8a452b (patch)
tree22a2f15923726f1d51ab96a71a95c2b8d8223fb0 /cyw43/src/control.rs
parent6b21f6d3d1f48bfa722d648918e06b627350bbff (diff)
cyw43: add support for WPA3 and more extensive security options.
Diffstat (limited to 'cyw43/src/control.rs')
-rw-r--r--cyw43/src/control.rs184
1 files changed, 133 insertions, 51 deletions
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs
index 97dcb4d09..071ba88e4 100644
--- a/cyw43/src/control.rs
+++ b/cyw43/src/control.rs
@@ -35,7 +35,7 @@ pub struct Control<'a> {
35 ioctl_state: &'a IoctlState, 35 ioctl_state: &'a IoctlState,
36} 36}
37 37
38#[derive(Copy, Clone)] 38#[derive(Copy, Clone, Debug)]
39#[cfg_attr(feature = "defmt", derive(defmt::Format))] 39#[cfg_attr(feature = "defmt", derive(defmt::Format))]
40pub enum ScanType { 40pub enum ScanType {
41 Active, 41 Active,
@@ -43,8 +43,9 @@ pub enum ScanType {
43} 43}
44 44
45/// Scan options. 45/// Scan options.
46#[derive(Clone)] 46#[derive(Clone, Debug)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))] 47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48#[non_exhaustive]
48pub struct ScanOptions { 49pub struct ScanOptions {
49 /// SSID to scan for. 50 /// SSID to scan for.
50 pub ssid: Option<heapless::String<32>>, 51 pub ssid: Option<heapless::String<32>>,
@@ -74,6 +75,79 @@ impl Default for ScanOptions {
74 } 75 }
75} 76}
76 77
78/// Authentication type, used in [`JoinOptions::auth`].
79#[derive(Copy, Clone, Debug, PartialEq, Eq)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81pub enum JoinAuth {
82 /// Open network
83 Open,
84 /// WPA only
85 Wpa,
86 /// WPA2 only
87 Wpa2,
88 /// WPA3 only
89 Wpa3,
90 /// WPA2 + WPA3
91 Wpa2Wpa3,
92}
93
94/// Options for [`Control::join`].
95#[derive(Clone, Debug)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97#[non_exhaustive]
98pub struct JoinOptions<'a> {
99 /// Authentication type. Default `Wpa2Wpa3`.
100 pub auth: JoinAuth,
101 /// Enable TKIP encryption. Default false.
102 pub cipher_tkip: bool,
103 /// Enable AES encryption. Default true.
104 pub cipher_aes: bool,
105 /// Passphrase. Default empty.
106 pub passphrase: &'a [u8],
107 /// If false, `passphrase` is the human-readable passphrase string.
108 /// If true, `passphrase` is the result of applying the PBKDF2 hash to the
109 /// passphrase string. This makes it possible to avoid storing unhashed passwords.
110 ///
111 /// This is not compatible with WPA3.
112 /// Default false.
113 pub passphrase_is_prehashed: bool,
114}
115
116impl<'a> JoinOptions<'a> {
117 /// Create a new `JoinOptions` for joining open networks.
118 pub fn new_open() -> Self {
119 Self {
120 auth: JoinAuth::Open,
121 cipher_tkip: false,
122 cipher_aes: false,
123 passphrase: &[],
124 passphrase_is_prehashed: false,
125 }
126 }
127
128 /// Create a new `JoinOptions` for joining encrypted networks.
129 ///
130 /// Defaults to supporting WPA2+WPA3 with AES only, you may edit
131 /// the returned options to change this.
132 pub fn new(passphrase: &'a [u8]) -> Self {
133 let mut this = Self::default();
134 this.passphrase = passphrase;
135 this
136 }
137}
138
139impl<'a> Default for JoinOptions<'a> {
140 fn default() -> Self {
141 Self {
142 auth: JoinAuth::Wpa2Wpa3,
143 cipher_tkip: false,
144 cipher_aes: true,
145 passphrase: &[],
146 passphrase_is_prehashed: false,
147 }
148 }
149}
150
77impl<'a> Control<'a> { 151impl<'a> Control<'a> {
78 pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { 152 pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
79 Self { 153 Self {
@@ -217,40 +291,70 @@ impl<'a> Control<'a> {
217 } 291 }
218 292
219 /// Join an unprotected network with the provided ssid. 293 /// Join an unprotected network with the provided ssid.
220 pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { 294 pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), Error> {
221 self.set_iovar_u32("ampdu_ba_wsize", 8).await; 295 self.set_iovar_u32("ampdu_ba_wsize", 8).await;
222 296
223 self.ioctl_set_u32(Ioctl::SetWsec, 0, 0).await; // wsec = open 297 if options.auth == JoinAuth::Open {
224 self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; 298 self.ioctl_set_u32(Ioctl::SetWsec, 0, 0).await;
225 self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await; // set_infra = 1 299 self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await;
226 self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await; // set_auth = open (0) 300 self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await;
227 301 self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await;
228 let mut i = SsidInfo { 302 self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, WPA_AUTH_DISABLED).await;
229 len: ssid.len() as _, 303 } else {
230 ssid: [0; 32], 304 let mut wsec = 0;
231 }; 305 if options.cipher_aes {
232 i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); 306 wsec |= WSEC_AES;
307 }
308 if options.cipher_tkip {
309 wsec |= WSEC_TKIP;
310 }
311 self.ioctl_set_u32(Ioctl::SetWsec, 0, wsec).await;
233 312
234 self.wait_for_join(i).await 313 self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await;
235 } 314 self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
315 self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
236 316
237 /// Join a protected network with the provided ssid and [`PassphraseInfo`]. 317 Timer::after_millis(100).await;
238 async fn join_wpa2_passphrase_info(&mut self, ssid: &str, passphrase_info: &PassphraseInfo) -> Result<(), Error> {
239 self.set_iovar_u32("ampdu_ba_wsize", 8).await;
240 318
241 self.ioctl_set_u32(Ioctl::SetWsec, 0, 4).await; // wsec = open 319 let (wpa12, wpa3, auth, mfp, wpa_auth) = match options.auth {
242 self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; 320 JoinAuth::Open => unreachable!(),
243 self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; 321 JoinAuth::Wpa => (true, false, AUTH_OPEN, MFP_NONE, WPA_AUTH_WPA_PSK),
244 self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; 322 JoinAuth::Wpa2 => (true, false, AUTH_OPEN, MFP_CAPABLE, WPA_AUTH_WPA2_PSK),
323 JoinAuth::Wpa3 => (false, true, AUTH_SAE, MFP_REQUIRED, WPA_AUTH_WPA3_SAE_PSK),
324 JoinAuth::Wpa2Wpa3 => (true, true, AUTH_SAE, MFP_CAPABLE, WPA_AUTH_WPA3_SAE_PSK),
325 };
245 326
246 Timer::after_millis(100).await; 327 if wpa12 {
328 let mut flags = 0;
329 if !options.passphrase_is_prehashed {
330 flags |= 1;
331 }
332 let mut pfi = PassphraseInfo {
333 len: options.passphrase.len() as _,
334 flags,
335 passphrase: [0; 64],
336 };
337 pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase);
338 Timer::after_millis(3).await;
339 self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes())
340 .await;
341 }
247 342
248 self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut passphrase_info.to_bytes()) 343 if wpa3 {
249 .await; // WLC_SET_WSEC_PMK 344 let mut pfi = SaePassphraseInfo {
345 len: options.passphrase.len() as _,
346 passphrase: [0; 128],
347 };
348 pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase);
349 Timer::after_millis(3).await;
350 self.set_iovar("sae_password", &pfi.to_bytes()).await;
351 }
250 352
251 self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await; // set_infra = 1 353 self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await;
252 self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await; // set_auth = 0 (open) 354 self.ioctl_set_u32(Ioctl::SetAuth, 0, auth).await;
253 self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, 0x80).await; 355 self.set_iovar_u32("mfp", mfp).await;
356 self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, wpa_auth).await;
357 }
254 358
255 let mut i = SsidInfo { 359 let mut i = SsidInfo {
256 len: ssid.len() as _, 360 len: ssid.len() as _,
@@ -261,28 +365,6 @@ impl<'a> Control<'a> {
261 self.wait_for_join(i).await 365 self.wait_for_join(i).await
262 } 366 }
263 367
264 /// Join a protected network with the provided ssid and passphrase.
265 pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
266 let mut pfi = PassphraseInfo {
267 len: passphrase.len() as _,
268 flags: 1,
269 passphrase: [0; 64],
270 };
271 pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes());
272 self.join_wpa2_passphrase_info(ssid, &pfi).await
273 }
274
275 /// Join a protected network with the provided ssid and precomputed PSK.
276 pub async fn join_wpa2_psk(&mut self, ssid: &str, psk: &[u8; 32]) -> Result<(), Error> {
277 let mut pfi = PassphraseInfo {
278 len: psk.len() as _,
279 flags: 0,
280 passphrase: [0; 64],
281 };
282 pfi.passphrase[..psk.len()].copy_from_slice(psk);
283 self.join_wpa2_passphrase_info(ssid, &pfi).await
284 }
285
286 async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { 368 async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> {
287 self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); 369 self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]);
288 let mut subscriber = self.events.queue.subscriber().unwrap(); 370 let mut subscriber = self.events.queue.subscriber().unwrap();
@@ -477,7 +559,7 @@ impl<'a> Control<'a> {
477 } 559 }
478 560
479 async fn set_iovar(&mut self, name: &str, val: &[u8]) { 561 async fn set_iovar(&mut self, name: &str, val: &[u8]) {
480 self.set_iovar_v::<64>(name, val).await 562 self.set_iovar_v::<196>(name, val).await
481 } 563 }
482 564
483 async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) { 565 async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) {