aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cyw43/src/consts.rs15
-rw-r--r--cyw43/src/control.rs184
-rw-r--r--cyw43/src/lib.rs4
-rw-r--r--cyw43/src/structs.rs9
-rw-r--r--examples/rp/src/bin/wifi_tcp_server.rs7
-rw-r--r--examples/rp/src/bin/wifi_webrequest.rs7
-rw-r--r--tests/rp/src/bin/cyw43-perf.rs6
7 files changed, 175 insertions, 57 deletions
diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs
index d47e5097a..c3f0dbfd8 100644
--- a/cyw43/src/consts.rs
+++ b/cyw43/src/consts.rs
@@ -653,3 +653,18 @@ pub(crate) enum Ioctl {
653 GetWsecPmk = 318, 653 GetWsecPmk = 318,
654 GetRandomBytes = 319, 654 GetRandomBytes = 319,
655} 655}
656
657pub(crate) const WSEC_TKIP: u32 = 0x02;
658pub(crate) const WSEC_AES: u32 = 0x04;
659
660pub(crate) const AUTH_OPEN: u32 = 0x00;
661pub(crate) const AUTH_SAE: u32 = 0x03;
662
663pub(crate) const MFP_NONE: u32 = 0;
664pub(crate) const MFP_CAPABLE: u32 = 1;
665pub(crate) const MFP_REQUIRED: u32 = 2;
666
667pub(crate) const WPA_AUTH_DISABLED: u32 = 0x0000;
668pub(crate) const WPA_AUTH_WPA_PSK: u32 = 0x0004;
669pub(crate) const WPA_AUTH_WPA2_PSK: u32 = 0x0080;
670pub(crate) const WPA_AUTH_WPA3_SAE_PSK: u32 = 0x40000;
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]) {
diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs
index efeb3f313..6b71c18e6 100644
--- a/cyw43/src/lib.rs
+++ b/cyw43/src/lib.rs
@@ -28,7 +28,9 @@ use ioctl::IoctlState;
28 28
29use crate::bus::Bus; 29use crate::bus::Bus;
30pub use crate::bus::SpiBusCyw43; 30pub use crate::bus::SpiBusCyw43;
31pub use crate::control::{AddMulticastAddressError, Control, Error as ControlError, ScanOptions, Scanner}; 31pub use crate::control::{
32 AddMulticastAddressError, Control, Error as ControlError, JoinAuth, JoinOptions, ScanOptions, Scanner,
33};
32pub use crate::runner::Runner; 34pub use crate::runner::Runner;
33pub use crate::structs::BssInfo; 35pub use crate::structs::BssInfo;
34 36
diff --git a/cyw43/src/structs.rs b/cyw43/src/structs.rs
index ae7ef6038..81ae6a98d 100644
--- a/cyw43/src/structs.rs
+++ b/cyw43/src/structs.rs
@@ -397,6 +397,15 @@ impl_bytes!(PassphraseInfo);
397#[derive(Clone, Copy)] 397#[derive(Clone, Copy)]
398#[cfg_attr(feature = "defmt", derive(defmt::Format))] 398#[cfg_attr(feature = "defmt", derive(defmt::Format))]
399#[repr(C)] 399#[repr(C)]
400pub struct SaePassphraseInfo {
401 pub len: u16,
402 pub passphrase: [u8; 128],
403}
404impl_bytes!(SaePassphraseInfo);
405
406#[derive(Clone, Copy)]
407#[cfg_attr(feature = "defmt", derive(defmt::Format))]
408#[repr(C)]
400pub struct SsidInfoWithIndex { 409pub struct SsidInfoWithIndex {
401 pub index: u32, 410 pub index: u32,
402 pub ssid_info: SsidInfo, 411 pub ssid_info: SsidInfo,
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs
index 61eeb82f7..b2950d98a 100644
--- a/examples/rp/src/bin/wifi_tcp_server.rs
+++ b/examples/rp/src/bin/wifi_tcp_server.rs
@@ -7,6 +7,7 @@
7 7
8use core::str::from_utf8; 8use core::str::from_utf8;
9 9
10use cyw43::JoinOptions;
10use cyw43_pio::PioSpi; 11use cyw43_pio::PioSpi;
11use defmt::*; 12use defmt::*;
12use embassy_executor::Spawner; 13use embassy_executor::Spawner;
@@ -95,8 +96,10 @@ async fn main(spawner: Spawner) {
95 unwrap!(spawner.spawn(net_task(stack))); 96 unwrap!(spawner.spawn(net_task(stack)));
96 97
97 loop { 98 loop {
98 //control.join_open(WIFI_NETWORK).await; 99 match control
99 match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { 100 .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
101 .await
102 {
100 Ok(_) => break, 103 Ok(_) => break,
101 Err(err) => { 104 Err(err) => {
102 info!("join failed with status={}", err.status); 105 info!("join failed with status={}", err.status);
diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs
index 889371241..b43be8905 100644
--- a/examples/rp/src/bin/wifi_webrequest.rs
+++ b/examples/rp/src/bin/wifi_webrequest.rs
@@ -7,6 +7,7 @@
7 7
8use core::str::from_utf8; 8use core::str::from_utf8;
9 9
10use cyw43::JoinOptions;
10use cyw43_pio::PioSpi; 11use cyw43_pio::PioSpi;
11use defmt::*; 12use defmt::*;
12use embassy_executor::Spawner; 13use embassy_executor::Spawner;
@@ -98,8 +99,10 @@ async fn main(spawner: Spawner) {
98 unwrap!(spawner.spawn(net_task(stack))); 99 unwrap!(spawner.spawn(net_task(stack)));
99 100
100 loop { 101 loop {
101 //match control.join_open(WIFI_NETWORK).await { // for open networks 102 match control
102 match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { 103 .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
104 .await
105 {
103 Ok(_) => break, 106 Ok(_) => break,
104 Err(err) => { 107 Err(err) => {
105 info!("join failed with status={}", err.status); 108 info!("join failed with status={}", err.status);
diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs
index 38fbde7c1..11c8aa58c 100644
--- a/tests/rp/src/bin/cyw43-perf.rs
+++ b/tests/rp/src/bin/cyw43-perf.rs
@@ -2,6 +2,7 @@
2#![no_main] 2#![no_main]
3teleprobe_meta::target!(b"rpi-pico"); 3teleprobe_meta::target!(b"rpi-pico");
4 4
5use cyw43::JoinOptions;
5use cyw43_pio::PioSpi; 6use cyw43_pio::PioSpi;
6use defmt::{panic, *}; 7use defmt::{panic, *};
7use embassy_executor::Spawner; 8use embassy_executor::Spawner;
@@ -81,7 +82,10 @@ async fn main(spawner: Spawner) {
81 unwrap!(spawner.spawn(net_task(stack))); 82 unwrap!(spawner.spawn(net_task(stack)));
82 83
83 loop { 84 loop {
84 match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { 85 match control
86 .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
87 .await
88 {
85 Ok(_) => break, 89 Ok(_) => break,
86 Err(err) => { 90 Err(err) => {
87 panic!("join failed with status={}", err.status); 91 panic!("join failed with status={}", err.status);