1use crate::Error;
6use anyhow::ensure;
7use core::num::NonZeroU32;
8use ieee80211::Ssid;
9use std::str;
10use wlan_common::security::wpa::credential::Psk as CommonPsk;
11use wlan_common::security::wpa::{self};
12
13#[allow(deprecated)]
15use mundane::insecure::insecure_pbkdf2_hmac_sha1;
16
17pub trait ToPsk {
19 fn to_psk(&self, ssid: &Ssid) -> CommonPsk;
20}
21
22impl ToPsk for wpa::Wpa1Credentials {
27 fn to_psk(&self, ssid: &Ssid) -> CommonPsk {
28 match self {
29 wpa::Wpa1Credentials::Psk(ref psk) => psk.clone(),
30 wpa::Wpa1Credentials::Passphrase(ref passphrase) => {
31 CommonPsk(
34 compute(passphrase.as_ref(), ssid)
35 .expect("invalid WPA passphrase data")
36 .as_ref()
37 .try_into()
38 .expect("invalid derived PSK data"),
39 )
40 }
41 }
42 }
43}
44
45impl ToPsk for wpa::Wpa2PersonalCredentials {
50 fn to_psk(&self, ssid: &Ssid) -> CommonPsk {
51 match self {
52 wpa::Wpa2PersonalCredentials::Psk(ref psk) => psk.clone(),
53 wpa::Wpa2PersonalCredentials::Passphrase(ref passphrase) => {
54 CommonPsk(
57 compute(passphrase.as_ref(), ssid)
58 .expect("invalid WPA passphrase data")
59 .as_ref()
60 .try_into()
61 .expect("invalid derived PSK data"),
62 )
63 }
64 }
65 }
66}
67
68pub type Psk = Box<[u8]>;
72
73pub fn compute(passphrase: &[u8], ssid: &Ssid) -> Result<Psk, anyhow::Error> {
74 let _utf8 = str::from_utf8(passphrase)
86 .map_err(|error| Error::InvalidPassphraseEncoding(error.valid_up_to()))?;
87
88 ensure!(
95 passphrase.len() >= 8 && passphrase.len() <= 63,
96 Error::InvalidPassphraseLen(passphrase.len())
97 );
98
99 let size = 256 / 8;
101 let mut psk = vec![0_u8; size];
102 const ITERS: NonZeroU32 = NonZeroU32::new(4096).unwrap();
103 #[allow(deprecated)]
105 insecure_pbkdf2_hmac_sha1(&passphrase[..], &ssid[..], ITERS, &mut psk[..]);
106 Ok(psk.into_boxed_slice())
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use hex::FromHex;
113 use wlan_common::security::wpa::credential::Passphrase;
114
115 fn assert_psk(password: &str, ssid: &str, expected: &str) {
116 let psk = compute(password.as_bytes(), &Ssid::try_from(ssid).unwrap())
117 .expect("computing PSK failed");
118 let expected = Vec::from_hex(expected).unwrap();
119 assert_eq!(&psk[..], &expected[..]);
120 }
121
122 #[test]
124 fn test_psk_test_case_1() {
125 assert_psk(
126 "password",
127 "IEEE",
128 "f42c6fc52df0ebef9ebb4b90b38a5f902e83fe1b135a70e23aed762e9710a12e",
129 );
130 }
131
132 #[test]
134 fn test_psk_test_case_2() {
135 assert_psk(
136 "ThisIsAPassword",
137 "ThisIsASSID",
138 "0dc0d6eb90555ed6419756b9a15ec3e3209b63df707dd508d14581f8982721af",
139 );
140 }
141
142 #[test]
144 fn test_psk_test_case_3() {
145 assert_psk(
146 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
147 "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
148 "becb93866bb8c3832cb777c2f559807c8c59afcb6eae734885001300a981cc62",
149 );
150 }
151
152 #[test]
153 fn test_psk_too_short_password() {
154 let result = compute("short".as_bytes(), &Ssid::try_from("Some SSID").unwrap());
155 assert!(result.is_err());
156 }
157
158 #[test]
159 fn test_psk_too_long_password() {
160 let result = compute(
161 "1234567890123456789012345678901234567890123456789012345678901234".as_bytes(),
162 &Ssid::try_from("Some SSID").unwrap(),
163 );
164 assert!(result.is_err());
165 }
166
167 #[test]
168 fn test_psk_ascii_bounds_password() {
169 let result =
170 compute("\x20ASCII Bound Test \x7E".as_bytes(), &Ssid::try_from("Some SSID").unwrap());
171 assert!(result.is_ok());
172 }
173
174 #[test]
175 fn test_psk_non_ascii_password() {
176 assert!(compute("パスワード".as_bytes(), &Ssid::try_from("Some SSID").unwrap()).is_ok());
177 }
178
179 #[test]
180 fn test_psk_invalid_encoding_password() {
181 assert!(compute(&[0xFFu8; 32], &Ssid::try_from("Some SSID").unwrap()).is_err());
182 }
183
184 #[test]
185 fn wpa1_credentials_to_psk() {
186 let ssid = Ssid::try_from("IEEE").unwrap();
187
188 let credentials = wpa::Wpa1Credentials::Psk(CommonPsk::from([0u8; 32]));
189 assert_eq!(CommonPsk::from([0u8; 32]), credentials.to_psk(&ssid));
190
191 let credentials =
192 wpa::Wpa1Credentials::Passphrase(Passphrase::try_from("password").unwrap());
193 assert_eq!(
194 CommonPsk::parse("f42c6fc52df0ebef9ebb4b90b38a5f902e83fe1b135a70e23aed762e9710a12e")
195 .unwrap(),
196 credentials.to_psk(&ssid),
197 );
198 }
199
200 #[test]
201 fn wpa2_personal_credentials_to_psk() {
202 let ssid = Ssid::try_from("IEEE").unwrap();
203
204 let credentials = wpa::Wpa2PersonalCredentials::Psk(CommonPsk::from([0u8; 32]));
205 assert_eq!(CommonPsk::from([0u8; 32]), credentials.to_psk(&ssid));
206
207 let credentials =
208 wpa::Wpa2PersonalCredentials::Passphrase(Passphrase::try_from("password").unwrap());
209 assert_eq!(
210 CommonPsk::parse("f42c6fc52df0ebef9ebb4b90b38a5f902e83fe1b135a70e23aed762e9710a12e")
211 .unwrap(),
212 credentials.to_psk(&ssid),
213 );
214 }
215}