credentials.rs
1 use embedded_storage::nor_flash::NorFlash; 2 use embedded_storage::ReadStorage; 3 use esp_storage::FlashStorage; 4 5 pub const DEFAULT_SSID: &str = env!("WIFI_SSID"); 6 pub const DEFAULT_PASSWORD: &str = env!("WIFI_PSK"); 7 8 const CREDENTIALS_MAGIC: u32 = 0xCE6A0001; 9 const CREDENTIALS_OFFSET: usize = 0x1000; 10 #[allow(dead_code, reason = "used by credential update API endpoint")] 11 const CREDENTIALS_SECTOR_SIZE: usize = 4096; 12 const SSID_MAX_LEN: usize = 32; 13 const PASSWORD_MAX_LEN: usize = 64; 14 15 #[repr(C)] 16 struct CredentialsRecord { 17 magic: u32, 18 ssid_len: u8, 19 password_len: u8, 20 ssid: [u8; SSID_MAX_LEN], 21 password: [u8; PASSWORD_MAX_LEN], 22 } 23 24 pub struct WifiCredentials { 25 pub ssid: heapless::String<SSID_MAX_LEN>, 26 pub password: heapless::String<PASSWORD_MAX_LEN>, 27 } 28 29 pub fn default_credentials() -> WifiCredentials { 30 WifiCredentials { 31 ssid: heapless::String::try_from(DEFAULT_SSID).unwrap(), 32 password: heapless::String::try_from(DEFAULT_PASSWORD).unwrap(), 33 } 34 } 35 36 pub fn read_from_flash(flash: &mut FlashStorage) -> Option<WifiCredentials> { 37 let mut buffer = [0u8; size_of::<CredentialsRecord>()]; 38 39 if flash.read(CREDENTIALS_OFFSET as u32, &mut buffer).is_err() { 40 return None; 41 } 42 43 let record: CredentialsRecord = 44 unsafe { core::ptr::read_unaligned(buffer.as_ptr() as *const _) }; 45 46 if record.magic != CREDENTIALS_MAGIC { 47 return None; 48 } 49 50 let ssid_len = record.ssid_len as usize; 51 let password_len = record.password_len as usize; 52 53 if ssid_len > SSID_MAX_LEN || password_len > PASSWORD_MAX_LEN || ssid_len == 0 { 54 return None; 55 } 56 57 let mut ssid = heapless::String::new(); 58 for &byte in &record.ssid[..ssid_len] { 59 if ssid.push(byte as char).is_err() { 60 break; 61 } 62 } 63 64 let mut password = heapless::String::new(); 65 for &byte in &record.password[..password_len] { 66 if password.push(byte as char).is_err() { 67 break; 68 } 69 } 70 71 Some(WifiCredentials { ssid, password }) 72 } 73 74 pub fn write_to_flash(flash: &mut FlashStorage, ssid: &str, password: &str) -> bool { 75 if ssid.len() > SSID_MAX_LEN || password.len() > PASSWORD_MAX_LEN || ssid.is_empty() { 76 return false; 77 } 78 79 let mut record = CredentialsRecord { 80 magic: CREDENTIALS_MAGIC, 81 ssid_len: ssid.len() as u8, 82 password_len: password.len() as u8, 83 ssid: [0u8; SSID_MAX_LEN], 84 password: [0u8; PASSWORD_MAX_LEN], 85 }; 86 87 record.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); 88 record.password[..password.len()].copy_from_slice(password.as_bytes()); 89 90 let mut buffer = [0xFFu8; CREDENTIALS_SECTOR_SIZE]; 91 let record_bytes = unsafe { 92 core::slice::from_raw_parts( 93 &record as *const CredentialsRecord as *const u8, 94 size_of::<CredentialsRecord>(), 95 ) 96 }; 97 buffer[..record_bytes.len()].copy_from_slice(record_bytes); 98 99 if NorFlash::erase( 100 flash, 101 CREDENTIALS_OFFSET as u32, 102 (CREDENTIALS_OFFSET + CREDENTIALS_SECTOR_SIZE) as u32, 103 ) 104 .is_err() 105 { 106 return false; 107 } 108 109 if flash.write(CREDENTIALS_OFFSET as u32, &buffer).is_err() { 110 return false; 111 } 112 113 if let Some(verified) = read_from_flash(flash) { 114 verified.ssid.as_str() == ssid && verified.password.as_str() == password 115 } else { 116 false 117 } 118 }