[wlan][rsn] Make nonce generator thread safe
This is a preparation for moving the nonce generator into the
station and spawn exactly one such generator per station per
system boot up.
Sharing a single instance of such a generator between multiple,
potentially concurrent handshakes requires it to be thread safe.
Test=wlan_rsn_lib_test_rustc
NET-1338
Change-Id: I9c929e9378862579d608c0dd095c5562c5217214
diff --git a/lib/rust/crates/wlan-rsn/src/crypto_utils/nonce.rs b/lib/rust/crates/wlan-rsn/src/crypto_utils/nonce.rs
index f8d7f89..a29efcd 100644
--- a/lib/rust/crates/wlan-rsn/src/crypto_utils/nonce.rs
+++ b/lib/rust/crates/wlan-rsn/src/crypto_utils/nonce.rs
@@ -7,36 +7,45 @@
use failure;
use num::bigint::{BigUint, RandBigInt};
use rand::OsRng;
+use std::sync::Mutex;
use time;
-#[derive(Debug, PartialEq)]
+// Thread-safe nonce generator.
+// According to IEEE Std 802.11-2016, 12.7.5 each STA should be configured with an initial, random
+// counter at system boot up time.
+#[derive(Debug)]
pub struct NonceReader {
- key_counter: BigUint,
+ key_counter: Mutex<BigUint>,
}
impl NonceReader {
- pub fn new(sta_addr: [u8; 6]) -> Result<NonceReader, failure::Error> {
+ pub fn new(sta_addr: &[u8]) -> Result<NonceReader, failure::Error> {
// Write time and STA's address to buffer for PRF-256.
// IEEE Std 802.11-2016, 12.7.5 recommends using a time in NTP format.
// Fuchsia has no support for NTP yet; instead use a regular timestamp.
// TODO(NET-430): Use time in NTP format once Fuchsia added support.
let mut buf = BytesMut::with_capacity(14);
buf.put_u64_le(time::precise_time_ns());
- buf.put_slice(&sta_addr[..]);
+ buf.put_slice(sta_addr);
let k = OsRng::new()?.gen_biguint(256).to_bytes_le();
let init = prf(&k[..], "Init Counter", &buf[..], 256)?;
Ok(NonceReader {
- key_counter: BigUint::from_bytes_le(&init[..]),
+ key_counter: Mutex::new(BigUint::from_bytes_le(&init[..])),
})
}
- pub fn next(&mut self) -> Vec<u8> {
- self.key_counter += 1u8;
+ pub fn next(&mut self) -> Result<Vec<u8>, failure::Error> {
+ match self.key_counter.lock() {
+ Err(_) => bail!("NonceReader lock is poisoned"),
+ Ok(mut counter) => {
+ *counter += 1u8;
- // Expand nonce if it's less than 32 bytes.
- let mut result = self.key_counter.to_bytes_le();
- result.resize(32, 0);
- result
+ // Expand nonce if it's less than 32 bytes.
+ let mut result = (*counter).to_bytes_le();
+ result.resize(32, 0);
+ Ok(result)
+ },
+ }
}
}
@@ -47,10 +56,10 @@
#[test]
fn test_next_nonce() {
let addr: [u8; 6] = [1, 2, 3, 4, 5, 6];
- let mut rdr = NonceReader::new(addr).expect("error creating NonceReader");
- let mut previous_nonce = rdr.next();
+ let mut rdr = NonceReader::new(&addr[..]).expect("error creating NonceReader");
+ let mut previous_nonce = rdr.next().expect("error generating nonce");
for _ in 0..300 {
- let nonce = rdr.next();
+ let nonce = rdr.next().expect("error generating nonce");
let nonce_int = BigUint::from_bytes_le(&nonce[..]);
let previous_nonce_int = BigUint::from_bytes_le(&previous_nonce[..]);
assert_eq!(nonce_int.gt(&previous_nonce_int), true);
diff --git a/lib/rust/crates/wlan-rsn/src/key/exchange/handshake/fourway/supplicant.rs b/lib/rust/crates/wlan-rsn/src/key/exchange/handshake/fourway/supplicant.rs
index 403ed74..cc100f2 100644
--- a/lib/rust/crates/wlan-rsn/src/key/exchange/handshake/fourway/supplicant.rs
+++ b/lib/rust/crates/wlan-rsn/src/key/exchange/handshake/fourway/supplicant.rs
@@ -29,7 +29,7 @@
-> Result<(eapol::KeyFrame, Ptk), failure::Error>
{
let anonce = &msg1.get().key_nonce;
- let snonce = shared.nonce_rdr.next();
+ let snonce = shared.nonce_rdr.next()?;
let rsne = &shared.cfg.s_rsne;
let akm = &rsne.akm_suites[0];
let cipher = &rsne.pairwise_cipher_suites[0];
@@ -239,7 +239,7 @@
}
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug)]
struct SharedState {
anonce: [u8; 32],
pmk: Vec<u8>,
@@ -249,6 +249,18 @@
nonce_rdr: NonceReader,
}
+impl PartialEq for SharedState {
+ fn eq(&self, other: &SharedState) -> bool {
+ // Exclude nonce generator from comparison. This code will soon change once the nonce
+ // generator moved further up the stack.
+ self.anonce == other.anonce &&
+ self.pmk == other.pmk &&
+ self.kek == other.kek &&
+ self.kck == other.kck &&
+ self.cfg == other.cfg
+ }
+}
+
#[derive(Debug, PartialEq)]
pub struct Supplicant {
shared: SharedState,
@@ -257,7 +269,7 @@
impl Supplicant {
pub fn new(cfg: fourway::Config, pmk: Vec<u8>) -> Result<Self, failure::Error> {
- let nonce_rdr = NonceReader::new(cfg.s_addr)?;
+ let nonce_rdr = NonceReader::new(&cfg.s_addr[..])?;
Ok(Supplicant {
state: Some(State::PtkInit(PtkInitState {})),
shared: SharedState {
diff --git a/lib/rust/crates/wlan-rsn/src/rsna/test_util.rs b/lib/rust/crates/wlan-rsn/src/rsna/test_util.rs
index 3de12a7..17d819e 100644
--- a/lib/rust/crates/wlan-rsn/src/rsna/test_util.rs
+++ b/lib/rust/crates/wlan-rsn/src/rsna/test_util.rs
@@ -130,7 +130,8 @@
}
pub fn get_nonce() -> Vec<u8> {
- NonceReader::new(S_ADDR).expect("error creating NonceReader").next()
+ NonceReader::new(&S_ADDR[..]).expect("error creating NonceReader")
+ .next().expect("error generating nonce")
}
pub fn get_akm() -> akm::Akm {