wlan_common/security/wpa/
credential.rs
1use std::fmt::Debug;
6use std::str;
7use thiserror::Error;
8
9pub const PSK_SIZE_BYTES: usize = 32;
10pub const PASSPHRASE_MIN_SIZE_BYTES: usize = 8;
11pub const PASSPHRASE_MAX_SIZE_BYTES: usize = 63;
12
13#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
14#[non_exhaustive]
15pub enum PskError {
16 #[error("invalid PSK size: {0} bytes")]
17 Size(usize),
18 #[error("invalid PSK encoding")]
19 Encoding,
20}
21
22#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
23#[non_exhaustive]
24pub enum PassphraseError {
25 #[error("invalid WPA passphrase size: {0} bytes")]
26 Size(usize),
27 #[error("invalid WPA passphrase encoding")]
28 Encoding,
29}
30
31#[derive(Clone, Debug, Eq, PartialEq)]
33#[repr(transparent)]
34pub struct Psk(pub [u8; PSK_SIZE_BYTES]);
35
36impl Psk {
37 pub fn parse(bytes: impl AsRef<[u8]>) -> Result<Self, PskError> {
48 let bytes = bytes.as_ref();
49 if bytes.len() == PSK_SIZE_BYTES * 2 {
50 let bytes = hex::decode(bytes).map_err(|_| PskError::Encoding)?;
51 Ok(Psk(bytes.try_into().unwrap()))
52 } else {
53 Psk::try_from(bytes)
54 }
55 }
56}
57
58impl AsRef<[u8]> for Psk {
59 fn as_ref(&self) -> &[u8] {
60 &self.0
61 }
62}
63
64impl From<[u8; PSK_SIZE_BYTES]> for Psk {
65 fn from(bytes: [u8; 32]) -> Self {
66 Psk(bytes)
67 }
68}
69
70impl From<Psk> for [u8; PSK_SIZE_BYTES] {
71 fn from(psk: Psk) -> Self {
72 psk.0
73 }
74}
75
76impl From<Psk> for Box<[u8]> {
77 fn from(psk: Psk) -> Self {
78 Vec::from(psk).into_boxed_slice()
79 }
80}
81
82impl From<Psk> for Vec<u8> {
83 fn from(psk: Psk) -> Self {
84 psk.0.into()
85 }
86}
87
88impl<'a> TryFrom<&'a [u8]> for Psk {
93 type Error = PskError;
94
95 fn try_from(bytes: &'a [u8]) -> Result<Self, PskError> {
96 let n = bytes.len();
97 let psk = Psk(bytes.try_into().map_err(|_| PskError::Size(n))?);
98 Ok(psk)
99 }
100}
101
102#[derive(Clone, Debug, Eq, PartialEq)]
106#[repr(transparent)]
107pub struct Passphrase {
108 text: String,
109}
110
111impl Passphrase {
112 pub fn try_write_with<F>(mut self, mut f: F) -> Result<Self, PassphraseError>
125 where
126 F: FnMut(&mut String),
127 {
128 f(&mut self.text);
129 Passphrase::check(&self.text)?;
130 Ok(self)
131 }
132
133 fn check(text: &str) -> Result<(), PassphraseError> {
134 let n = text.as_bytes().len();
135 if n < PASSPHRASE_MIN_SIZE_BYTES || n > PASSPHRASE_MAX_SIZE_BYTES {
136 return Err(PassphraseError::Size(n));
137 }
138 Ok(())
139 }
140}
141
142impl AsRef<[u8]> for Passphrase {
143 fn as_ref(&self) -> &[u8] {
144 &self.text.as_bytes()
145 }
146}
147
148impl AsRef<str> for Passphrase {
149 fn as_ref(&self) -> &str {
150 &self.text
151 }
152}
153
154impl From<Passphrase> for Vec<u8> {
155 fn from(passphrase: Passphrase) -> Self {
156 passphrase.text.into_bytes()
157 }
158}
159
160impl From<Passphrase> for String {
161 fn from(passphrase: Passphrase) -> Self {
162 passphrase.text
163 }
164}
165
166impl<'a> TryFrom<&'a [u8]> for Passphrase {
167 type Error = PassphraseError;
168
169 fn try_from(bytes: &'a [u8]) -> Result<Self, PassphraseError> {
170 let text = str::from_utf8(bytes).map_err(|_| PassphraseError::Encoding)?;
171 Passphrase::check(text.as_ref())?;
172 Ok(Passphrase { text: text.to_owned() })
173 }
174}
175
176impl<'a> TryFrom<&'a str> for Passphrase {
177 type Error = PassphraseError;
178
179 fn try_from(text: &'a str) -> Result<Self, PassphraseError> {
180 Passphrase::check(text)?;
181 Ok(Passphrase { text: text.to_owned() })
182 }
183}
184
185impl TryFrom<String> for Passphrase {
186 type Error = PassphraseError;
187
188 fn try_from(text: String) -> Result<Self, PassphraseError> {
189 Passphrase::check(text.as_ref())?;
190 Ok(Passphrase { text })
191 }
192}
193
194impl TryFrom<Vec<u8>> for Passphrase {
195 type Error = PassphraseError;
196
197 fn try_from(bytes: Vec<u8>) -> Result<Self, PassphraseError> {
198 let bytes: &[u8] = bytes.as_ref();
199 Passphrase::try_from(bytes)
200 }
201}
202
203#[cfg(test)]
204mod tests {
205
206 use crate::security::wpa::credential::{
207 Passphrase, PassphraseError, Psk, PskError, PSK_SIZE_BYTES,
208 };
209
210 #[test]
211 fn convert_passphrase_bad_encoding() {
212 assert!(matches!(
213 Passphrase::try_from([0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF].as_ref()),
214 Err(PassphraseError::Encoding)
215 ));
216 }
217
218 #[test]
219 fn passphrase_bad_size() {
220 assert!(matches!(Passphrase::try_from("tiny"), Err(PassphraseError::Size(4))));
221 assert!(matches!(
222 Passphrase::try_from(
223 "huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuge"
224 ),
225 Err(PassphraseError::Size(65))
226 ));
227
228 let passphrase = Passphrase::try_from("itsasecret").unwrap();
229 assert!(matches!(
230 passphrase.try_write_with(|text| {
231 *text = "tiny".to_string();
232 }),
233 Err(PassphraseError::Size(4))
234 ));
235 }
236
237 #[test]
238 fn parse_psk() {
239 assert_eq!(
241 Psk::parse("therearethirtytwobytesineverypsk").unwrap(),
242 Psk(*b"therearethirtytwobytesineverypsk")
243 );
244 assert_eq!(
246 Psk::parse("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(),
247 Psk::from([0xFF; PSK_SIZE_BYTES])
248 );
249 }
250
251 #[test]
252 fn parse_psk_bad_size() {
253 assert!(matches!(Psk::parse(b"lolwut"), Err(PskError::Size(6))));
254 }
255
256 #[test]
257 fn parse_psk_bad_encoding() {
258 assert!(matches!(
259 Psk::parse("ZZFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
260 Err(PskError::Encoding)
261 ));
262 }
263}