From 15fd47d441f43ace057ae4b382f4088fe0ef241b Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 20 Apr 2026 17:14:30 +1000 Subject: [PATCH 1/5] Cleanup HSM state return values. Panic rather than software_reset upon failure. Remove unused disable fuctions. --- Cargo.lock | 2 +- src/espressif/net.rs | 23 ------------ src/main.rs | 89 ++++++++++++++++++++------------------------ 3 files changed, 41 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8829a1a..1f537b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1993,7 +1993,7 @@ dependencies = [ [[package]] name = "ssh-stamp" -version = "0.2.0" +version = "0.3.0" dependencies = [ "bcrypt", "cfg-if", diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 3a6436b..e6e9761 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -149,18 +149,6 @@ pub async fn if_up( Ok(ap_stack) } -pub fn ap_stack_disable() { - // drop ap_stack - debug!("AP Stack disabled: WIP"); - // TODO: Correctly disable/restart AP Stack and/or send messsage to user over SSH -} - -pub fn tcp_socket_disable() { - // drop tcp stack - debug!("TCP socket disabled: WIP"); - // TODO: Correctly disable/restart tcp socket and/or send messsage to user over SSH -} - pub async fn accept_requests<'a>( tcp_stack: Stack<'a>, rx_buffer: &'a mut [u8], @@ -178,7 +166,6 @@ pub async fn accept_requests<'a>( { error!("connect error: {e:?}"); // continue; - tcp_socket_disable(); } debug!("Connected, port 22"); @@ -267,16 +254,6 @@ pub async fn wifi_up( } } -pub fn wifi_controller_disable() { - // TODO: Correctly disable wifi controller - // pub async fn wifi_disable(wifi_controller: EspWifiController<'_>) -> (){ - // drop wifi controller - // esp_wifi::deinit_unchecked() - // wifi_controller.deinit_unchecked() - debug!("Disabling wifi: WIP"); - //software_reset(); -} - use esp_radio::wifi::{Config, WifiDevice}; #[embassy_executor::task] async fn net_up(mut runner: Runner<'static, WifiDevice<'static>>) { diff --git a/src/main.rs b/src/main.rs index 1e73144..00e40a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ // // SPDX-License-Identifier: GPL-3.0-or-later -use log::{debug, error, warn}; +use log::{debug, error}; use ssh_stamp::{ config::SSHStampConfig, espressif::{ @@ -36,7 +36,6 @@ use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::interrupt::{Priority, software::SoftwareInterruptControl}; use esp_hal::peripherals::WIFI; use esp_hal::rng::{Rng, TrngSource}; -use esp_hal::system::software_reset; use esp_println::logger; @@ -52,11 +51,6 @@ cfg_if::cfg_if! { } } -pub fn peripherals_disable() { - // drop peripherals - debug!("Disabling peripherals: WIP"); -} - pub struct SshStampInit<'a> { pub rng: Rng, pub wifi: WIFI<'a>, @@ -82,7 +76,7 @@ async fn main(spawner: Spawner) -> ! { debug!("HSM: Initialising peripherals "); // System init - let peripherals = esp_hal::init(esp_hal::Config::default()); + let peripherals = esp_hal::init(esp_hal::Config::default()); // !todo() panic!("Peripheral initialisation error!"); on peripherial initialsation error // Enable true random number generation using ADC entropy source before config creation. // The ESP32 hardware RNG only produces true random numbers when RF subsystem is enabled @@ -207,10 +201,7 @@ async fn main(spawner: Spawner) -> ! { } } - peripherals_disable(); - // loop {} - warn!("End of Main... Reset!!"); - software_reset(); + panic!("End of Main: Peripheral error!"); } pub struct PeripheralsEnabled<'a> { @@ -224,25 +215,27 @@ pub struct PeripheralsEnabled<'a> { async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Error> { debug!("HSM: peripherals_enabled"); - let controller = esp_radio::init().map_err(|_| sunset::error::BadUsage.build())?; + let controller = esp_radio::init().map_err(|e| panic!("Could not acquire esp_radio: {:?}", e)); let peripherals_enabled_struct = PeripheralsEnabled { rng: s.rng, wifi: s.wifi, config: s.config, - controller, + controller: controller.unwrap(), uart_buf: s.uart_buf, spawner: s.spawner, }; + match Box::pin(wifi_controller_enabled(peripherals_enabled_struct)).await { - Ok(()) => (), + Ok(_) => { + debug!("Disabling wifi"); + Ok(()) + } Err(e) => { - error!("Wifi controller error: {e}"); + error!("Wifi controller error: {}", e); + Result::Err(e) } } - - net::wifi_controller_disable(); - Ok(()) // todo!() return relevant value } pub struct WifiControllerEnabled<'a> { @@ -266,14 +259,17 @@ pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<( uart_buf: s.uart_buf, tcp_stack, }; + match Box::pin(tcp_enabled(wifi_controller_enabled_stack)).await { - Ok(()) => (), + Ok(_) => { + debug!("AP Stack disabled"); + Ok(()) + } Err(e) => { error!("AP Stack error: {e}"); + Result::Err(e) } } - net::ap_stack_disable(); - Ok(()) // todo!() return relevant value } pub struct TCPEnabled<'a> { @@ -285,7 +281,6 @@ pub struct TCPEnabled<'a> { cfg_if::cfg_if!(if #[cfg(feature = "esp32")] {use embassy_net::IpListenEndpoint;}); async fn tcp_enabled(s: WifiControllerEnabled<'_>) -> Result<(), sunset::Error> { debug!("HSM: tcp_enabled"); - let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; loop { @@ -302,7 +297,6 @@ async fn tcp_enabled(s: WifiControllerEnabled<'_>) -> Result<(), sunset::Error> .await { error!("connect error: {:?}", e); - net::tcp_socket_disable(); } debug!("Connected, port 22"); } else { @@ -316,14 +310,14 @@ async fn tcp_enabled(s: WifiControllerEnabled<'_>) -> Result<(), sunset::Error> uart_buf: s.uart_buf, }; match Box::pin(socket_enabled(tcp_enabled_struct)).await { - Ok(()) => (), + Ok(_) => { + debug!("TCP socket disabled"); + } Err(e) => { error!("TCP socket error: {e}"); } } - net::tcp_socket_disable(); } - // Ok(()) // todo!() return relevant value } pub struct SocketEnabled<'a> { @@ -335,7 +329,6 @@ pub struct SocketEnabled<'a> { async fn socket_enabled(s: TCPEnabled<'_>) -> Result<(), sunset::Error> { debug!("HSM: socket_enabled"); - // loop { // Spawn network tasks to handle incoming connections with demo_common::session() let mut inbuf = [0u8; UART_BUFFER_SIZE]; let mut outbuf = [0u8; UART_BUFFER_SIZE]; @@ -350,15 +343,15 @@ async fn socket_enabled(s: TCPEnabled<'_>) -> Result<(), sunset::Error> { uart_buf: s.uart_buf, }; match ssh_enabled(socket_enabled_struct).await { - Ok(()) => (), + Ok(_) => { + debug!("SSH Server disabled"); + Ok(()) + } Err(e) => { - error!("SSH server error: {e}"); + error!("SSH server error: {}", e); + Result::Err(e) } } - - serve::ssh_disable(); - // } - Ok(()) // todo!() return relevant value } pub struct SshEnabled<'a, 'b, CL> @@ -374,9 +367,7 @@ where } async fn ssh_enabled(s: SocketEnabled<'_>) -> Result<(), sunset::Error> { - debug!("HSM: ssh_enabled"); - // loop { - debug!("HSM: Starting channel pipe"); + debug!("HSM: ssh_enabled. Starting channel pipe"); let chan_pipe = Channel::::new(); debug!("HSM: Started channel pipe. Calling connection_loop from ssh_enabled"); let connection = serve::connection_loop(&s.ssh_server, &chan_pipe, s.config); @@ -391,15 +382,15 @@ async fn ssh_enabled(s: SocketEnabled<'_>) -> Result<(), sunset::Error> { connection_loop: connection, }; match client_connected(ssh_enabled_struct).await { - Ok(()) => (), + Ok(_) => { + debug!("Client connection disabled"); + Ok(()) + } Err(e) => { - error!("Client connection error: {e}"); + error!("Client connection error: {}", e); + Result::Err(e) } } - - serve::connection_disable(); - // } - Ok(()) // todo!() return relevant value } pub struct ClientConnected<'a, 'b, CL, BR> @@ -430,15 +421,15 @@ where tcp_socket: s.tcp_socket, }; match bridge_connected(uart_enabled_struct).await { - Ok(()) => (), + Ok(_) => { + debug!("Bridge disabled"); + Ok(()) + } Err(e) => { - debug!("Bridge error: {e}"); + debug!("Bridge error: {}", e); + Result::Err(e) } } - - serve::bridge_disable(); - - Ok(()) } async fn bridge_connected<'a, 'b, CL, BR>( From ba10eb44bf2743678a6ee6d4bab11d483ed8993a Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 20 Apr 2026 17:56:07 +1000 Subject: [PATCH 2/5] Fix clippy errors from HSM changes. --- src/main.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 00e40a5..0317817 100644 --- a/src/main.rs +++ b/src/main.rs @@ -227,12 +227,12 @@ async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Err }; match Box::pin(wifi_controller_enabled(peripherals_enabled_struct)).await { - Ok(_) => { + Ok(()) => { debug!("Disabling wifi"); Ok(()) } Err(e) => { - error!("Wifi controller error: {}", e); + error!("Wifi controller error: {e}"); Result::Err(e) } } @@ -261,7 +261,7 @@ pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<( }; match Box::pin(tcp_enabled(wifi_controller_enabled_stack)).await { - Ok(_) => { + Ok(()) => { debug!("AP Stack disabled"); Ok(()) } @@ -310,7 +310,7 @@ async fn tcp_enabled(s: WifiControllerEnabled<'_>) -> Result<(), sunset::Error> uart_buf: s.uart_buf, }; match Box::pin(socket_enabled(tcp_enabled_struct)).await { - Ok(_) => { + Ok(()) => { debug!("TCP socket disabled"); } Err(e) => { @@ -343,12 +343,12 @@ async fn socket_enabled(s: TCPEnabled<'_>) -> Result<(), sunset::Error> { uart_buf: s.uart_buf, }; match ssh_enabled(socket_enabled_struct).await { - Ok(_) => { + Ok(()) => { debug!("SSH Server disabled"); Ok(()) } Err(e) => { - error!("SSH server error: {}", e); + error!("SSH server error: {e}"); Result::Err(e) } } @@ -382,12 +382,12 @@ async fn ssh_enabled(s: SocketEnabled<'_>) -> Result<(), sunset::Error> { connection_loop: connection, }; match client_connected(ssh_enabled_struct).await { - Ok(_) => { + Ok(()) => { debug!("Client connection disabled"); Ok(()) } Err(e) => { - error!("Client connection error: {}", e); + error!("Client connection error: {e}"); Result::Err(e) } } @@ -421,12 +421,12 @@ where tcp_socket: s.tcp_socket, }; match bridge_connected(uart_enabled_struct).await { - Ok(_) => { + Ok(()) => { debug!("Bridge disabled"); Ok(()) } Err(e) => { - debug!("Bridge error: {}", e); + error!("Bridge error: {e}"); Result::Err(e) } } From b763df4b546af01f3b17583246d21079c2e9e3df Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 1 Jun 2026 10:46:44 +1000 Subject: [PATCH 3/5] Add ESP32 Wifi Station Mode. If Station SSID is set then ESP will be configured as station. Otherwise will default to Access Point Mode. --- src/app.rs | 12 +-- src/config.rs | 45 ++++++++--- src/handle.rs | 94 ++++++++++++++++++---- src/store.rs | 10 +-- ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs | 18 +++-- ssh-stamp-esp32/src/network/wifi.rs | 90 +++++++++++++++------ ssh-stamp-hal/src/config.rs | 13 ++- 7 files changed, 212 insertions(+), 70 deletions(-) diff --git a/src/app.rs b/src/app.rs index 95d7fb7..426dfc8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -51,16 +51,16 @@ pub async fn prepare_ap_config( ) -> Result { let mut guard = config.lock().await; - if guard.wifi_pw.is_empty() { + if guard.wifi_ap_pw.is_empty() { let pw = generate_wifi_password()?; warn!("wifi_pw missing from config, generated new password"); - guard.wifi_pw = pw; + guard.wifi_ap_pw = pw; platform .save_config(&guard) .await .map_err(|_| sunset::error::BadUsage.build())?; } - info!("WIFI PSK: {}", guard.wifi_pw); + info!("WIFI PSK: {}", guard.wifi_ap_pw); let mac = guard .resolve_mac() @@ -73,8 +73,10 @@ pub async fn prepare_ap_config( print_hostkey_fingerprint(&guard.hostkey); Ok(WifiApConfigStatic { - ssid: guard.wifi_ssid.clone(), - password: guard.wifi_pw.clone(), + ap_ssid: guard.wifi_ap_ssid.clone(), + ap_password: guard.wifi_ap_pw.clone(), + sta_ssid: guard.wifi_sta_ssid.clone(), + sta_password: guard.wifi_sta_pw.clone(), channel: 1, mac, }) diff --git a/src/config.rs b/src/config.rs index 82e1b0e..7354204 100644 --- a/src/config.rs +++ b/src/config.rs @@ -35,8 +35,12 @@ pub struct SSHStampConfig { pub pubkeys: [Option; KEY_SLOTS], /// `WiFi` - pub wifi_ssid: String<32>, - pub wifi_pw: String<63>, + /// Access Point Mode + pub wifi_ap_ssid: String<32>, + pub wifi_ap_pw: String<63>, + /// Station Mode + pub wifi_sta_ssid: String<32>, + pub wifi_sta_pw: String<63>, /// Networking /// MAC address. Special values: @@ -103,9 +107,14 @@ impl SSHStampConfig { pub fn new(default_mac: [u8; 6]) -> Result { let hostkey = SignKey::generate(KeyType::Ed25519, None)?; - let wifi_ssid = Self::generate_wifi_ssid()?; + // Wifi Access Point Mode + let wifi_ap_ssid = Self::generate_wifi_ssid()?; + let wifi_ap_pw = Self::generate_wifi_password()?; + // Wifi Station Mode + let wifi_sta_ssid = Self::generate_wifi_ssid()?; + let wifi_sta_pw = Self::generate_wifi_password()?; + let mac = default_mac; - let wifi_pw = Self::generate_wifi_password()?; let uart_pins = UartPins::default(); debug!( @@ -116,8 +125,10 @@ impl SSHStampConfig { Ok(SSHStampConfig { hostkey, pubkeys: Default::default(), - wifi_ssid, - wifi_pw, + wifi_ap_ssid, + wifi_ap_pw, + wifi_sta_ssid, + wifi_sta_pw, mac, ipv4_static: None, #[cfg(feature = "ipv6")] @@ -310,8 +321,12 @@ impl SSHEncode for SSHStampConfig { enc_option(k.as_ref(), s)?; } - self.wifi_ssid.as_str().enc(s)?; - self.wifi_pw.as_str().enc(s)?; + // Wifi Access Point Mode + self.wifi_ap_ssid.as_str().enc(s)?; + self.wifi_ap_pw.as_str().enc(s)?; + // Wifi Station Mode + self.wifi_sta_ssid.as_str().enc(s)?; + self.wifi_sta_pw.as_str().enc(s)?; self.mac.enc(s)?; enc_ipv4_config(self.ipv4_static.as_ref(), s)?; @@ -341,8 +356,12 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { *k = dec_option(s)?; } - let wifi_ssid = SSHDecode::dec(s)?; - let wifi_pw = SSHDecode::dec(s)?; + // Wifi Access Point Mode + let wifi_ap_ssid = SSHDecode::dec(s)?; + let wifi_ap_pw = SSHDecode::dec(s)?; + // Wifi Station Mode + let wifi_sta_ssid = SSHDecode::dec(s)?; + let wifi_sta_pw = SSHDecode::dec(s)?; let mac = SSHDecode::dec(s)?; @@ -361,8 +380,10 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { Ok(Self { hostkey, pubkeys, - wifi_ssid, - wifi_pw, + wifi_ap_ssid, + wifi_ap_pw, + wifi_sta_ssid, + wifi_sta_pw, mac, ipv4_static, #[cfg(feature = "ipv6")] diff --git a/src/handle.rs b/src/handle.rs index 017b61c..76ee130 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -352,11 +352,17 @@ pub async fn session_env( "SSH_STAMP_PUBKEY" => { pubkey_env(a, config, ctx).await?; } - "SSH_STAMP_WIFI_SSID" => { - wifi_ssid_env(a, config, ctx).await?; + "SSH_STAMP_WIFI_AP_SSID" => { + wifi_ap_ssid_env(a, config, ctx).await?; } - "SSH_STAMP_WIFI_PSK" => { - wifi_psk_env(a, config, ctx).await?; + "SSH_STAMP_WIFI_AP_PSK" => { + wifi_ap_psk_env(a, config, ctx).await?; + } + "SSH_STAMP_WIFI_STA_SSID" => { + wifi_sta_ssid_env(a, config, ctx).await?; + } + "SSH_STAMP_WIFI_STA_PW" => { + wifi_sta_psk_env(a, config, ctx).await?; } "SSH_STAMP_WIFI_MAC_ADDRESS" => { wifi_mac_address_env(a, config, ctx).await?; @@ -414,11 +420,11 @@ pub async fn pubkey_env( Ok(()) } -/// Handles `SSH_STAMP_WIFI_SSID` environment variable requests. +/// Handles `SSH_STAMP_WIFI_AP_SSID` environment variable requests. /// /// # Errors /// Returns an error if SSH protocol operations fail or if the SSID is invalid. -pub async fn wifi_ssid_env( +pub async fn wifi_ap_ssid_env( a: sunset::event::ServEnvironmentRequest<'_, '_>, config: &SunsetMutex, ctx: &mut EventContext<'_>, @@ -426,27 +432,83 @@ pub async fn wifi_ssid_env( let mut config_guard = config.lock().await; if *ctx.auth_checked || config_guard.first_login { if let Some(s) = env_parser::parse_wifi_ssid(a.value()?) { - config_guard.wifi_ssid = s; - debug!("Set wifi SSID from ENV"); + config_guard.wifi_ap_ssid = s; + debug!("Set wifi Access Point SSID from ENV"); a.succeed()?; *ctx.config_changed = true; *ctx.needs_reset = true; } else { - warn!("SSH_STAMP_WIFI_SSID invalid and/or too long"); + warn!("SSH_STAMP_WIFI_AP_SSID invalid and/or too long"); a.fail()?; } } else { - warn!("SSH_STAMP_WIFI_SSID env received but not authenticated; rejecting"); + warn!("SSH_STAMP_WIFI_AP_SSID env received but not authenticated; rejecting"); a.fail()?; } Ok(()) } -/// Handles `SSH_STAMP_WIFI_PSK` environment variable requests. +/// Handles `SSH_STAMP_WIFI_AP_PSK` environment variable requests. /// /// # Errors /// Returns an error if SSH protocol operations fail or if the PSK is invalid. -pub async fn wifi_psk_env( +pub async fn wifi_ap_psk_env( + a: sunset::event::ServEnvironmentRequest<'_, '_>, + config: &SunsetMutex, + ctx: &mut EventContext<'_>, +) -> Result<(), sunset::Error> { + let mut config_guard = config.lock().await; + if *ctx.auth_checked || config_guard.first_login { + if let Some(s) = env_parser::parse_wifi_psk(a.value()?) { + config_guard.wifi_ap_pw = s; + debug!("Set WIFI AP PSK from ENV"); + a.succeed()?; + *ctx.config_changed = true; + *ctx.needs_reset = true; + } else { + warn!("SSH_STAMP_WIFI_AP_PSK invalid and/or not within 8-63 characters"); + a.fail()?; + } + } else { + warn!("SSH_STAMP_WIFI_AP_PSK env received but not authenticated; rejecting"); + a.fail()?; + } + Ok(()) +} + +/// Handles `SSH_STAMP_WIFI_STA_SSID` environment variable requests. +/// +/// # Errors +/// Returns an error if SSH protocol operations fail or if the SSID is invalid. +pub async fn wifi_sta_ssid_env( + a: sunset::event::ServEnvironmentRequest<'_, '_>, + config: &SunsetMutex, + ctx: &mut EventContext<'_>, +) -> Result<(), sunset::Error> { + let mut config_guard = config.lock().await; + if *ctx.auth_checked || config_guard.first_login { + if let Some(s) = env_parser::parse_wifi_ssid(a.value()?) { + config_guard.wifi_sta_ssid = s; + debug!("Set wifi STATION SSID from ENV"); + a.succeed()?; + *ctx.config_changed = true; + *ctx.needs_reset = true; + } else { + warn!("SSH_STAMP_WIFI_STA_SSID invalid and/or too long"); + a.fail()?; + } + } else { + warn!("SSH_STAMP_WIFI_STA_SSID env received but not authenticated; rejecting"); + a.fail()?; + } + Ok(()) +} + +/// Handles `SSH_STAMP_WIFI_STA_PSK` environment variable requests. +/// +/// # Errors +/// Returns an error if SSH protocol operations fail or if the SSID is invalid. +pub async fn wifi_sta_psk_env( a: sunset::event::ServEnvironmentRequest<'_, '_>, config: &SunsetMutex, ctx: &mut EventContext<'_>, @@ -454,17 +516,17 @@ pub async fn wifi_psk_env( let mut config_guard = config.lock().await; if *ctx.auth_checked || config_guard.first_login { if let Some(s) = env_parser::parse_wifi_psk(a.value()?) { - config_guard.wifi_pw = s; - debug!("Set WIFI PSK from ENV"); + config_guard.wifi_sta_pw = s; + debug!("Set wifi STATION PSK from ENV"); a.succeed()?; *ctx.config_changed = true; *ctx.needs_reset = true; } else { - warn!("SSH_STAMP_WIFI_PSK invalid and/or not within 8-63 characters"); + warn!("SSH_STAMP_WIFI_STA_PSK invalid and/or not within 8-63 characters"); a.fail()?; } } else { - warn!("SSH_STAMP_WIFI_PSK env received but not authenticated; rejecting"); + warn!("SSH_STAMP_WIFI_STA_PSK env received but not authenticated; rejecting"); a.fail()?; } Ok(()) diff --git a/src/store.rs b/src/store.rs index 32323df..168b55b 100644 --- a/src/store.rs +++ b/src/store.rs @@ -62,11 +62,11 @@ where match load(flash, buf) { Ok(mut c) => { debug!("Good existing config"); - if c.wifi_ssid.as_str() == "ssh-stamp" { - debug!("Migrating insecure default SSID, regenerating randomly"); - c.wifi_ssid = SSHStampConfig::generate_wifi_ssid()?; - if c.wifi_pw.is_empty() { - c.wifi_pw = SSHStampConfig::generate_wifi_password()?; + if c.wifi_ap_ssid.as_str() == "ssh-stamp" { + debug!("Migrating insecure default Access Point SSID, regenerating randomly"); + c.wifi_ap_ssid = SSHStampConfig::generate_wifi_ssid()?; + if c.wifi_ap_pw.is_empty() { + c.wifi_ap_pw = SSHStampConfig::generate_wifi_password()?; } save(flash, buf, &c)?; } diff --git a/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs b/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs index e96f7fb..d66cd46 100644 --- a/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs +++ b/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs @@ -162,11 +162,19 @@ async fn main(spawner: Spawner) -> ! { let ap_config = app::prepare_ap_config(config, &platform) .await .expect("Failed to prepare AP config"); - info!( - "Connect to the AP `{}` as a DHCP client with IP: {}", - ap_config.ssid.as_str(), - DEFAULT_IP - ); + if ap_config.sta_ssid.as_str() != "" { + info!( + "SSH Stamp has connected to Access Point {}. Connect to the same Access Point as a DHCP client with IP: {}", + ap_config.sta_ssid.as_str(), + DEFAULT_IP + ); + } else { + info!( + "Connect to the AP `{}` as a DHCP client with IP: {}", + ap_config.ap_ssid.as_str(), + DEFAULT_IP + ); + } let mut wifi = EspWifi::new(spawner, peripherals.WIFI, rng, DEFAULT_IP); wifi.configure_ap(ap_config) diff --git a/ssh-stamp-esp32/src/network/wifi.rs b/ssh-stamp-esp32/src/network/wifi.rs index 15864ec..1bda71b 100644 --- a/ssh-stamp-esp32/src/network/wifi.rs +++ b/ssh-stamp-esp32/src/network/wifi.rs @@ -30,7 +30,7 @@ use esp_hal::peripherals::WIFI; use esp_hal::rng::Rng; use esp_radio::wifi::{ AuthenticationMethod, Config as RadioConfig, ControllerConfig, Interface as WifiInterface, - WifiController, ap::AccessPointConfig, + WifiController, ap::AccessPointConfig, sta::StationConfig, }; use log::{debug, error, warn}; use ssh_stamp_hal::{HalError, NetworkProviderHal, WifiApConfigStatic, WifiError, WifiHal}; @@ -79,8 +79,10 @@ impl WifiHal for EspWifi { impl NetworkProviderHal for EspWifi { async fn bring_up(&mut self) -> Result, HalError> { static RESOURCES_CELL: StaticCell> = StaticCell::new(); - static SSID_CELL: StaticCell> = StaticCell::new(); - static PASSWORD_CELL: StaticCell> = StaticCell::new(); + static AP_SSID_CELL: StaticCell> = StaticCell::new(); + static AP_PASSWORD_CELL: StaticCell> = StaticCell::new(); + static STA_SSID_CELL: StaticCell> = StaticCell::new(); + static STA_PASSWORD_CELL: StaticCell> = StaticCell::new(); let ap_config = self .ap_config @@ -95,13 +97,38 @@ impl NetworkProviderHal for EspWifi { esp_hal::efuse::override_mac_address(esp_hal::efuse::MacAddress::new_eui48(ap_config.mac)) .map_err(|_| HalError::Wifi(WifiError::Initialization))?; - let password = AllocString::from(ap_config.password.as_str()); - let ap_radio_config = RadioConfig::AccessPoint( - AccessPointConfig::default() - .with_ssid(AllocString::from(ap_config.ssid.as_str())) - .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) - .with_password(password.clone()), - ); + let ap_ssid_static: &'static str = AP_SSID_CELL.init(ap_config.ap_ssid.clone()).as_str(); + let ap_password_static: &'static str = AP_PASSWORD_CELL + .init(ap_config.ap_password.clone()) + .as_str(); + let sta_ssid_static: &'static str = STA_SSID_CELL.init(ap_config.sta_ssid.clone()).as_str(); + let sta_password_static: &'static str = STA_PASSWORD_CELL + .init(ap_config.sta_password.clone()) + .as_str(); + + let ap_radio_config; + let ap_password; + let sta_password; + + if sta_ssid_static != "" { + // Client/Station Mode + sta_password = AllocString::from(ap_config.sta_password.as_str()); + ap_radio_config = RadioConfig::Station( + StationConfig::default() + .with_ssid(AllocString::from(ap_config.sta_ssid.as_str())) + .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) + .with_password(AllocString::from(sta_password)), + ); + } else { + // Default to Access Point mode + ap_password = AllocString::from(ap_config.ap_password.as_str()); + ap_radio_config = RadioConfig::AccessPoint( + AccessPointConfig::default() + .with_ssid(AllocString::from(ap_config.ap_ssid.as_str())) + .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) + .with_password(AllocString::from(ap_password)), + ); + } let controller_config = ControllerConfig::default().with_initial_config(ap_radio_config); let (wifi_controller, interfaces) = esp_radio::wifi::new(wifi_peri, controller_config) @@ -122,12 +149,15 @@ impl NetworkProviderHal for EspWifi { seed, ); - let ssid_static: &'static str = SSID_CELL.init(ap_config.ssid.clone()).as_str(); - let password_static: &'static str = PASSWORD_CELL.init(ap_config.password.clone()).as_str(); - self.spawner.spawn( - wifi_up(wifi_controller, ssid_static, password_static) - .map_err(|_| HalError::Wifi(WifiError::Initialization))?, + wifi_up( + wifi_controller, + ap_ssid_static, + ap_password_static, + sta_ssid_static, + sta_password_static, + ) + .map_err(|_| HalError::Wifi(WifiError::Initialization))?, ); self.spawner .spawn(net_up(runner).map_err(|_| HalError::Wifi(WifiError::Initialization))?); @@ -185,15 +215,29 @@ pub async fn accept_requests<'a>( #[embassy_executor::task] pub async fn wifi_up( mut wifi_controller: WifiController<'static>, - ssid: &'static str, - password: &'static str, + ap_ssid: &'static str, + ap_password: &'static str, + sta_ssid: &'static str, + sta_password: &'static str, ) { - let ap_config = RadioConfig::AccessPoint( - AccessPointConfig::default() - .with_ssid(AllocString::from(ssid)) - .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) - .with_password(AllocString::from(password)), - ); + let ap_config; + if sta_ssid != "" { + // Client/Station Mode + ap_config = RadioConfig::Station( + StationConfig::default() + .with_ssid(AllocString::from(sta_ssid)) + .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) + .with_password(AllocString::from(sta_password)), + ); + } else { + // Default to Access Point mode + ap_config = RadioConfig::AccessPoint( + AccessPointConfig::default() + .with_ssid(AllocString::from(ap_ssid)) + .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) + .with_password(AllocString::from(ap_password)), + ); + } loop { match wifi_controller.set_config(&ap_config) { diff --git a/ssh-stamp-hal/src/config.rs b/ssh-stamp-hal/src/config.rs index e0ebae5..6d90f7d 100644 --- a/ssh-stamp-hal/src/config.rs +++ b/ssh-stamp-hal/src/config.rs @@ -35,12 +35,15 @@ impl Default for UartConfig { /// Contains settings for running the device as a `WiFi` access point. #[derive(Clone, Debug)] pub struct WifiApConfigStatic { + /// Wifi Mode - Access Point (ap) or Station (sta) Mode. Access Point by default. /// Network name (SSID), max 32 characters. - pub ssid: String<32>, + pub ap_ssid: String<32>, + pub sta_ssid: String<32>, /// Mandatory `WiFi` password, max 63 characters. /// We don't want None here as it would present an open network, /// which is not something we want to support. - pub password: String<63>, + pub ap_password: String<63>, + pub sta_password: String<63>, /// `WiFi` channel (1-14 for 2.4GHz). pub channel: u8, /// MAC address for the access point interface. @@ -50,8 +53,10 @@ pub struct WifiApConfigStatic { impl Default for WifiApConfigStatic { fn default() -> Self { Self { - ssid: String::new(), - password: String::new(), + ap_ssid: String::new(), + ap_password: String::new(), + sta_ssid: String::new(), + sta_password: String::new(), channel: 1, mac: [0; 6], } From 1a870981f1aeed09d909523d8e4d4bd7746bc2a2 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:09:42 +1000 Subject: [PATCH 4/5] Cleanup clippy errors using is_empty(). --- ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs | 10 ++--- ssh-stamp-esp32/src/network/wifi.rs | 47 +++++++++++----------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs b/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs index d66cd46..574afd9 100644 --- a/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs +++ b/ssh-stamp-esp32/src/bin/ssh-stamp-esp32.rs @@ -162,16 +162,16 @@ async fn main(spawner: Spawner) -> ! { let ap_config = app::prepare_ap_config(config, &platform) .await .expect("Failed to prepare AP config"); - if ap_config.sta_ssid.as_str() != "" { + if ap_config.sta_ssid.as_str().is_empty() { info!( - "SSH Stamp has connected to Access Point {}. Connect to the same Access Point as a DHCP client with IP: {}", - ap_config.sta_ssid.as_str(), + "Connect to the AP `{}` as a DHCP client with IP: {}", + ap_config.ap_ssid.as_str(), DEFAULT_IP ); } else { info!( - "Connect to the AP `{}` as a DHCP client with IP: {}", - ap_config.ap_ssid.as_str(), + "SSH Stamp has connected to Access Point {}. Connect to the same Access Point as a DHCP client with IP: {}", + ap_config.sta_ssid.as_str(), DEFAULT_IP ); } diff --git a/ssh-stamp-esp32/src/network/wifi.rs b/ssh-stamp-esp32/src/network/wifi.rs index 1bda71b..ad2f708 100644 --- a/ssh-stamp-esp32/src/network/wifi.rs +++ b/ssh-stamp-esp32/src/network/wifi.rs @@ -110,23 +110,23 @@ impl NetworkProviderHal for EspWifi { let ap_password; let sta_password; - if sta_ssid_static != "" { - // Client/Station Mode - sta_password = AllocString::from(ap_config.sta_password.as_str()); - ap_radio_config = RadioConfig::Station( - StationConfig::default() - .with_ssid(AllocString::from(ap_config.sta_ssid.as_str())) - .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) - .with_password(AllocString::from(sta_password)), - ); - } else { + if sta_ssid_static.is_empty() { // Default to Access Point mode ap_password = AllocString::from(ap_config.ap_password.as_str()); ap_radio_config = RadioConfig::AccessPoint( AccessPointConfig::default() .with_ssid(AllocString::from(ap_config.ap_ssid.as_str())) .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) - .with_password(AllocString::from(ap_password)), + .with_password(ap_password), + ); + } else { + // Client/Station Mode + sta_password = AllocString::from(ap_config.sta_password.as_str()); + ap_radio_config = RadioConfig::Station( + StationConfig::default() + .with_ssid(AllocString::from(ap_config.sta_ssid.as_str())) + .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) + .with_password(sta_password ), ); } @@ -220,24 +220,23 @@ pub async fn wifi_up( sta_ssid: &'static str, sta_password: &'static str, ) { - let ap_config; - if sta_ssid != "" { - // Client/Station Mode - ap_config = RadioConfig::Station( - StationConfig::default() - .with_ssid(AllocString::from(sta_ssid)) - .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) - .with_password(AllocString::from(sta_password)), - ); - } else { + let ap_config = if sta_ssid.is_empty() { // Default to Access Point mode - ap_config = RadioConfig::AccessPoint( + RadioConfig::AccessPoint( AccessPointConfig::default() .with_ssid(AllocString::from(ap_ssid)) .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) .with_password(AllocString::from(ap_password)), - ); - } + ) + } else { + // Client/Station Mode + RadioConfig::Station( + StationConfig::default() + .with_ssid(AllocString::from(sta_ssid)) + .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) + .with_password(AllocString::from(sta_password)), + ) + }; loop { match wifi_controller.set_config(&ap_config) { From 4fe9e56f9773ed7e249972ffba28e1404e7c09b6 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:16:06 +1000 Subject: [PATCH 5/5] Update wifi.rs --- ssh-stamp-esp32/src/network/wifi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssh-stamp-esp32/src/network/wifi.rs b/ssh-stamp-esp32/src/network/wifi.rs index ad2f708..74fd9e4 100644 --- a/ssh-stamp-esp32/src/network/wifi.rs +++ b/ssh-stamp-esp32/src/network/wifi.rs @@ -126,7 +126,7 @@ impl NetworkProviderHal for EspWifi { StationConfig::default() .with_ssid(AllocString::from(ap_config.sta_ssid.as_str())) .with_auth_method(AuthenticationMethod::Wpa2Wpa3Personal) - .with_password(sta_password ), + .with_password(sta_password), ); }