mod boringssl;
mod ecc;
mod frame;
pub mod hmac_utils;
mod state;
use anyhow::{bail, Error};
use boringssl::{Bignum, EcGroupId};
use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
pub use frame::{AntiCloggingTokenMsg, CommitMsg, ConfirmMsg};
use hmac_utils::{HmacUtils, HmacUtilsImpl};
use ieee80211::{MacAddr, Ssid};
use log::warn;
use mundane::hash::Sha256;
use num::FromPrimitive;
use wlan_common::ie::rsn::akm::{self, Akm};
const MAX_RETRIES_PER_EXCHANGE: u16 = 3;
#[derive(Clone, PartialEq, Debug)]
pub struct Key {
pub pmk: Vec<u8>,
pub pmkid: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PweMethod {
Loop = 0,
Direct = 1,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Timeout {
Retransmission,
KeyExpiration,
}
#[derive(Debug)]
pub enum RejectReason {
InternalError(Error),
AuthFailed,
TooManyRetries,
KeyExpiration,
}
impl From<Error> for RejectReason {
fn from(e: Error) -> Self {
Self::InternalError(e)
}
}
#[derive(Debug)]
pub struct AuthFrameRx<'a> {
pub seq: u16,
pub status_code: fidl_ieee80211::StatusCode,
pub body: &'a [u8],
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct AuthFrameTx {
pub seq: u16,
pub status_code: fidl_ieee80211::StatusCode,
pub body: Vec<u8>,
}
#[derive(Debug)]
pub enum SaeUpdate {
SendFrame(AuthFrameTx),
Success(Key),
Reject(RejectReason),
ResetTimeout(Timeout),
CancelTimeout(Timeout),
}
pub type SaeUpdateSink = Vec<SaeUpdate>;
pub trait SaeHandshake: Send {
fn initiate_sae(&mut self, sink: &mut SaeUpdateSink);
fn handle_commit(&mut self, sink: &mut SaeUpdateSink, commit_msg: &CommitMsg<'_>);
fn handle_confirm(&mut self, sink: &mut SaeUpdateSink, confirm_msg: &ConfirmMsg<'_>);
fn handle_anti_clogging_token(
&mut self,
sink: &mut SaeUpdateSink,
act_msg: &AntiCloggingTokenMsg<'_>,
);
fn handle_timeout(&mut self, sink: &mut SaeUpdateSink, timeout: Timeout);
fn handle_frame(&mut self, sink: &mut SaeUpdateSink, frame: &AuthFrameRx<'_>) {
match frame::parse(frame) {
Ok(parse) => match parse {
frame::ParseSuccess::Commit(commit) => self.handle_commit(sink, &commit),
frame::ParseSuccess::Confirm(confirm) => self.handle_confirm(sink, &confirm),
frame::ParseSuccess::AntiCloggingToken(act_msg) => {
self.handle_anti_clogging_token(sink, &act_msg)
}
},
Err(e) => warn!("Failed to parse SAE auth frame: {}", e),
}
}
}
pub fn new_sae_handshake(
group_id: u16,
akm: Akm,
pwe_method: PweMethod,
ssid: Ssid,
password: Vec<u8>,
password_id: Option<Vec<u8>>,
mac: MacAddr,
peer_mac: MacAddr,
) -> Result<Box<dyn SaeHandshake>, Error> {
match akm.suite_type {
akm::SAE | akm::FT_SAE => (),
_ => bail!("Cannot construct SAE handshake with AKM {:?}", akm),
};
let (hmac, group_constructor) = match EcGroupId::from_u16(group_id) {
Some(EcGroupId::P256) => {
let hmac = Box::new(HmacUtilsImpl::<Sha256>::new());
let group_constructor = Box::new(|| {
ecc::Group::new(EcGroupId::P256).map(|group| {
Box::new(group)
as Box<
dyn internal::FiniteCyclicGroup<
Element = <ecc::Group as internal::FiniteCyclicGroup>::Element,
>,
>
})
});
(hmac, group_constructor)
}
_ => bail!("Unsupported SAE group id: {}", group_id),
};
Ok(Box::new(state::SaeHandshakeImpl::new(
group_constructor,
internal::SaeParameters {
hmac,
pwe_method,
ssid,
password,
password_id,
sta_a_mac: mac,
sta_b_mac: peer_mac,
},
)?))
}
pub fn join_sae_handshake(
sink: &mut SaeUpdateSink,
first_frame: &AuthFrameRx<'_>,
akm: Akm,
ssid: Ssid,
password: Vec<u8>,
mac: MacAddr,
peer_mac: MacAddr,
) -> Result<Box<dyn SaeHandshake>, Error> {
let parsed_frame = frame::parse(first_frame)?;
match parsed_frame {
frame::ParseSuccess::Commit(commit) => {
let mut handshake = new_sae_handshake(
commit.group_id,
akm,
PweMethod::Loop,
ssid,
password,
None,
mac,
peer_mac,
)?;
handshake.handle_commit(sink, &commit);
Ok(handshake)
}
_ => bail!("Recieved incorrect first frame of SAE handshake"),
}
}
mod internal {
use super::*;
pub trait FiniteCyclicGroup {
type Element;
fn group_id(&self) -> u16;
fn generate_pwe(&self, params: &SaeParameters) -> Result<Self::Element, Error>;
fn scalar_op(
&self,
scalar: &Bignum,
element: &Self::Element,
) -> Result<Self::Element, Error>;
fn elem_op(
&self,
element1: &Self::Element,
element2: &Self::Element,
) -> Result<Self::Element, Error>;
fn inverse_op(&self, element: Self::Element) -> Result<Self::Element, Error>;
fn order(&self) -> Result<Bignum, Error>;
fn map_to_secret_value(&self, element: &Self::Element) -> Result<Option<Vec<u8>>, Error>;
fn element_to_octets(&self, element: &Self::Element) -> Result<Vec<u8>, Error>;
fn element_from_octets(&self, octets: &[u8]) -> Result<Option<Self::Element>, Error>;
fn scalar_size(&self) -> Result<usize, Error> {
self.order().map(|order| order.len())
}
}
pub struct SaeParameters {
pub hmac: Box<dyn HmacUtils + Send>,
pub pwe_method: PweMethod,
pub ssid: Ssid,
pub password: Vec<u8>,
pub password_id: Option<Vec<u8>>,
pub sta_a_mac: MacAddr,
pub sta_b_mac: MacAddr,
}
}
#[cfg(test)]
mod tests {
#![allow(unused_variables)] use super::*;
use lazy_static::lazy_static;
use std::convert::TryFrom;
use wlan_common::assert_variant;
use wlan_common::ie::rsn::akm::{AKM_PSK, AKM_SAE};
const TEST_SSID: &'static str = "SSID not in 802.11-2016";
const TEST_PWD: &'static str = "thisisreallysecret";
lazy_static! {
static ref TEST_STA_A: MacAddr = MacAddr::from([0x7b, 0x88, 0x56, 0x20, 0x2d, 0x8d]);
static ref TEST_STA_B: MacAddr = MacAddr::from([0xe2, 0x47, 0x1c, 0x0a, 0x5a, 0xcb]);
}
#[test]
fn bad_akm() {
let akm = AKM_PSK;
let res = new_sae_handshake(
19,
akm,
PweMethod::Loop,
Ssid::try_from(TEST_SSID).unwrap(),
Vec::from(TEST_PWD),
None, *TEST_STA_A,
*TEST_STA_B,
);
assert!(res.is_err());
assert!(format!("{}", res.err().unwrap())
.contains("Cannot construct SAE handshake with AKM 00-0F-AC:2"));
}
#[test]
fn bad_fcg() {
let akm = AKM_SAE;
let res = new_sae_handshake(
200,
akm,
PweMethod::Loop,
Ssid::try_from(TEST_SSID).unwrap(),
Vec::from(TEST_PWD),
None, *TEST_STA_A,
*TEST_STA_B,
);
assert!(res.is_err());
assert!(format!("{}", res.err().unwrap()).contains("Unsupported SAE group id: 200"));
}
struct TestHandshake {
sta1: Box<dyn SaeHandshake>,
sta2: Box<dyn SaeHandshake>,
}
#[derive(Clone, Eq, PartialEq, Debug)]
struct CommitTx(AuthFrameTx);
#[derive(Clone, Eq, PartialEq, Debug)]
struct ConfirmTx(AuthFrameTx);
struct CommitRx<'a>(AuthFrameRx<'a>);
struct ConfirmRx<'a>(AuthFrameRx<'a>);
fn to_rx(frame: &AuthFrameTx) -> AuthFrameRx<'_> {
AuthFrameRx { seq: frame.seq, status_code: frame.status_code, body: &frame.body[..] }
}
impl CommitTx {
fn to_rx(&self) -> CommitRx<'_> {
CommitRx(to_rx(&self.0))
}
}
impl ConfirmTx {
fn to_rx(&self) -> ConfirmRx<'_> {
ConfirmRx(to_rx(&self.0))
}
}
impl<'a> CommitRx<'a> {
fn msg(&'a self) -> CommitMsg<'a> {
assert_variant!(frame::parse(&self.0),
Ok(frame::ParseSuccess::Commit(commit)) => commit)
}
}
impl<'a> ConfirmRx<'a> {
fn msg(&'a self) -> ConfirmMsg<'a> {
assert_variant!(frame::parse(&self.0),
Ok(frame::ParseSuccess::Confirm(confirm)) => confirm)
}
}
fn expect_commit(sink: &mut Vec<SaeUpdate>) -> CommitTx {
let commit = assert_variant!(sink.remove(0), SaeUpdate::SendFrame(frame) => frame);
assert_variant!(frame::parse(&to_rx(&commit)), Ok(frame::ParseSuccess::Commit(msg)));
CommitTx(commit)
}
fn expect_confirm(sink: &mut Vec<SaeUpdate>) -> ConfirmTx {
let confirm = assert_variant!(sink.remove(0), SaeUpdate::SendFrame(frame) => frame);
assert_variant!(frame::parse(&to_rx(&confirm)), Ok(frame::ParseSuccess::Confirm(msg)));
ConfirmTx(confirm)
}
fn expect_reset_timeout(sink: &mut Vec<SaeUpdate>, timeout: Timeout) {
assert_variant!(sink.remove(0), SaeUpdate::ResetTimeout(timeout));
}
fn expect_cancel_timeout(sink: &mut Vec<SaeUpdate>, timeout: Timeout) {
assert_variant!(sink.remove(0), SaeUpdate::CancelTimeout(timeout));
}
impl TestHandshake {
fn new() -> Self {
let akm = AKM_SAE;
let sta1 = new_sae_handshake(
19,
akm.clone(),
PweMethod::Loop,
Ssid::try_from(TEST_SSID).unwrap(),
Vec::from(TEST_PWD),
None, *TEST_STA_A,
*TEST_STA_B,
)
.unwrap();
let sta2 = new_sae_handshake(
19,
akm,
PweMethod::Loop,
Ssid::try_from(TEST_SSID).unwrap(),
Vec::from(TEST_PWD),
None, *TEST_STA_B,
*TEST_STA_A,
)
.unwrap();
Self { sta1, sta2 }
}
fn sta1_init(&mut self) -> CommitTx {
let mut sink = vec![];
self.sta1.initiate_sae(&mut sink);
assert_eq!(sink.len(), 2);
let commit = expect_commit(&mut sink);
expect_reset_timeout(&mut sink, Timeout::Retransmission);
commit
}
fn sta2_handle_commit(&mut self, commit1: CommitRx<'_>) -> (CommitTx, ConfirmTx) {
let mut sink = vec![];
self.sta2.handle_commit(&mut sink, &commit1.msg());
assert_eq!(sink.len(), 3);
let commit2 = expect_commit(&mut sink);
let confirm2 = expect_confirm(&mut sink);
expect_reset_timeout(&mut sink, Timeout::Retransmission);
(commit2, confirm2)
}
fn sta1_handle_commit(&mut self, commit2: CommitRx<'_>) -> ConfirmTx {
let mut sink = vec![];
self.sta1.handle_commit(&mut sink, &commit2.msg());
assert_eq!(sink.len(), 2);
let confirm1 = expect_confirm(&mut sink);
expect_reset_timeout(&mut sink, Timeout::Retransmission);
confirm1
}
fn sta1_handle_confirm(&mut self, confirm2: ConfirmRx<'_>) -> Key {
Self::__internal_handle_confirm(&mut self.sta1, confirm2.msg())
}
fn sta2_handle_confirm(&mut self, confirm1: ConfirmRx<'_>) -> Key {
Self::__internal_handle_confirm(&mut self.sta2, confirm1.msg())
}
fn __internal_handle_confirm(
sta: &mut Box<dyn SaeHandshake>,
confirm: ConfirmMsg<'_>,
) -> Key {
let mut sink = vec![];
sta.handle_confirm(&mut sink, &confirm);
assert_eq!(sink.len(), 3);
expect_cancel_timeout(&mut sink, Timeout::Retransmission);
expect_reset_timeout(&mut sink, Timeout::KeyExpiration);
assert_variant!(sink.remove(0), SaeUpdate::Success(key) => key)
}
}
#[test]
fn sae_handshake_success() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
let key2 = handshake.sta2_handle_confirm(confirm1.to_rx());
assert_eq!(key1, key2);
}
#[test]
fn password_mismatch() {
let akm = AKM_SAE;
let sta1 = new_sae_handshake(
19,
akm.clone(),
PweMethod::Loop,
Ssid::try_from(TEST_SSID).unwrap(),
Vec::from(TEST_PWD),
None, *TEST_STA_A,
*TEST_STA_B,
)
.unwrap();
let sta2 = new_sae_handshake(
19,
akm,
PweMethod::Loop,
Ssid::try_from(TEST_SSID).unwrap(),
Vec::from("other_pwd"),
None, *TEST_STA_B,
*TEST_STA_A,
)
.unwrap();
let mut handshake = TestHandshake { sta1, sta2 };
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
let mut sink1 = vec![];
handshake.sta1.handle_confirm(&mut sink1, &confirm2.to_rx().msg());
let mut sink2 = vec![];
handshake.sta2.handle_confirm(&mut sink2, &confirm1.to_rx().msg());
assert_eq!(sink1.len(), 0);
assert_eq!(sink2.len(), 0);
}
#[test]
fn retry_commit_on_unexpected_confirm() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
let mut sink = vec![];
handshake.sta1.handle_confirm(&mut sink, &confirm2.to_rx().msg());
assert_eq!(sink.len(), 2);
let commit1_retry = expect_commit(&mut sink);
assert_variant!(sink.remove(0), SaeUpdate::ResetTimeout(Timeout::Retransmission));
assert_eq!(commit1, commit1_retry);
}
#[test]
fn retry_commit_on_anti_clogging_token() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let mut sink = vec![];
let anti_clogging_token = "anticloggingtokentext";
let act_msg = AntiCloggingTokenMsg {
group_id: 19,
anti_clogging_token: anti_clogging_token.as_bytes(),
};
handshake.sta1.handle_anti_clogging_token(&mut sink, &act_msg);
let commit1_retry = expect_commit(&mut sink);
assert_eq!(
commit1_retry.clone().to_rx().msg().anti_clogging_token,
Some(anti_clogging_token.as_bytes())
);
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1_retry.to_rx());
let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
let key2 = handshake.sta2_handle_confirm(confirm1.to_rx());
assert_eq!(key1, key2);
}
#[test]
fn ignore_wrong_confirm() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
let mut sink = vec![];
let confirm2_wrong = ConfirmTx(frame::write_confirm(1, &[1; 32][..]));
handshake.sta1.handle_confirm(&mut sink, &confirm2_wrong.to_rx().msg());
assert_eq!(sink.len(), 0); handshake.sta1_handle_confirm(confirm2.to_rx());
}
#[test]
fn handle_resent_commit() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
let (commit2_retry, confirm2_retry) = handshake.sta2_handle_commit(commit1.to_rx());
assert_eq!(commit2, commit2_retry);
assert_eq!(confirm2.to_rx().msg().send_confirm, 1);
assert_eq!(confirm2_retry.to_rx().msg().send_confirm, 2);
assert!(confirm2.to_rx().msg().confirm != confirm2_retry.to_rx().msg().confirm);
let confirm1 = handshake.sta1_handle_commit(commit2_retry.to_rx());
let key1 = handshake.sta1_handle_confirm(confirm2_retry.to_rx());
let key2 = handshake.sta2_handle_confirm(confirm1.to_rx());
assert_eq!(key1, key2);
}
#[test]
fn completed_handshake_handles_resent_confirm() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
let (commit2_retry, confirm2_retry) = handshake.sta2_handle_commit(commit1.to_rx());
let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
let key1 = handshake.sta1_handle_confirm(confirm2.clone().to_rx());
let mut sink = vec![];
handshake.sta1.handle_confirm(&mut sink, &confirm2_retry.to_rx().msg());
assert_eq!(sink.len(), 1);
let confirm1_retry = expect_confirm(&mut sink);
assert!(confirm1.to_rx().msg().confirm != confirm1_retry.to_rx().msg().confirm);
assert_eq!(confirm1_retry.to_rx().msg().send_confirm, u16::max_value());
let key2 = handshake.sta2_handle_confirm(confirm1_retry.to_rx());
assert_eq!(key1, key2);
handshake.sta1.handle_confirm(&mut sink, &confirm2_retry.to_rx().msg());
assert!(sink.is_empty());
handshake.sta1.handle_confirm(&mut sink, &confirm2.to_rx().msg());
assert!(sink.is_empty());
let confirm2_wrong = ConfirmMsg { send_confirm: 10, confirm: &[0xab; 32][..] };
handshake.sta1.handle_confirm(&mut sink, &confirm2_wrong);
assert!(sink.is_empty());
}
#[test]
fn completed_handshake_ignores_commit() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
handshake.sta1_handle_commit(commit2.to_rx());
handshake.sta1_handle_confirm(confirm2.clone().to_rx());
let mut sink = vec![];
handshake.sta1.handle_confirm(&mut sink, &confirm2.to_rx().msg());
assert!(sink.is_empty());
}
#[test]
fn bad_first_commit_rejects_auth() {
let mut handshake = TestHandshake::new();
let commit1_wrong = CommitMsg {
group_id: 19,
scalar: &[0xab; 32][..],
element: &[0xcd; 64][..],
anti_clogging_token: None,
};
let mut sink = vec![];
handshake.sta1.handle_commit(&mut sink, &commit1_wrong);
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::AuthFailed));
}
#[test]
fn bad_second_commit_ignored() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (_commit1, _confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
let commit2_wrong = CommitMsg {
group_id: 19,
scalar: &[0xab; 32][..],
element: &[0xcd; 64][..],
anti_clogging_token: None,
};
let mut sink = vec![];
handshake.sta1.handle_commit(&mut sink, &commit2_wrong);
assert_eq!(sink.len(), 0);
}
#[test]
fn reflected_commit_discarded() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let mut sink = vec![];
handshake.sta1.handle_commit(&mut sink, &commit1.to_rx().msg());
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::ResetTimeout(Timeout::Retransmission));
}
#[test]
fn maximum_commit_retries() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
for i in 0..MAX_RETRIES_PER_EXCHANGE {
let (commit2_retry, confirm2_retry) =
handshake.sta2_handle_commit(commit1.clone().to_rx());
assert_eq!(commit2, commit2_retry);
assert_eq!(confirm2_retry.to_rx().msg().send_confirm, i + 2);
}
let mut sink = vec![];
handshake.sta2.handle_commit(&mut sink, &commit1.to_rx().msg());
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
}
#[test]
fn completed_exchange_fails_after_retries() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
for i in 0..(MAX_RETRIES_PER_EXCHANGE - 1) {
let (commit2_retry, confirm2_retry) =
handshake.sta2_handle_commit(commit1.clone().to_rx());
assert_eq!(commit2, commit2_retry);
assert_eq!(confirm2_retry.to_rx().msg().send_confirm, i + 2);
}
let mut sink = vec![];
let confirm1_sc1 = handshake.sta1_handle_commit(commit2.clone().to_rx());
handshake.sta1.handle_commit(&mut sink, &commit2.to_rx().msg());
assert_eq!(sink.len(), 3);
sink.remove(0);
let confirm1_sc2 = expect_confirm(&mut sink);
sink.clear();
let confirm1_invalid = ConfirmMsg { send_confirm: 3, confirm: &[0xab; 32][..] };
handshake.sta2_handle_confirm(confirm1_sc1.clone().to_rx());
handshake.sta2.handle_commit(&mut sink, &commit1.to_rx().msg());
handshake.sta2.handle_commit(&mut sink, &commit1.to_rx().msg());
assert_eq!(sink.len(), 0);
handshake.sta2.handle_confirm(&mut sink, &confirm1_invalid);
handshake.sta2.handle_confirm(&mut sink, &confirm1_invalid);
assert_eq!(sink.len(), 0);
handshake.sta2.handle_confirm(&mut sink, &confirm1_sc1.to_rx().msg());
handshake.sta2.handle_confirm(&mut sink, &confirm1_sc1.to_rx().msg());
assert_eq!(sink.len(), 0);
handshake.sta2.handle_confirm(&mut sink, &confirm1_sc2.to_rx().msg());
assert_eq!(sink.len(), 1);
expect_confirm(&mut sink);
handshake.sta2.handle_confirm(&mut sink, &confirm1_sc2.to_rx().msg());
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
}
#[test]
fn resend_commit_after_retransmission_timeout() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let mut sink = vec![];
handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
let commit1_retry = expect_commit(&mut sink);
expect_reset_timeout(&mut sink, Timeout::Retransmission);
assert_eq!(commit1, commit1_retry);
}
#[test]
fn resend_confirm_after_retransmission_timeout() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
let mut sink = vec![];
handshake.sta2.handle_timeout(&mut sink, Timeout::Retransmission);
let confirm2_retry = expect_confirm(&mut sink);
expect_reset_timeout(&mut sink, Timeout::Retransmission);
assert_eq!(
confirm2.to_rx().msg().send_confirm + 1,
confirm2_retry.to_rx().msg().send_confirm
);
}
#[test]
fn abort_commit_after_too_many_timeouts() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let mut sink = vec![];
for i in 0..MAX_RETRIES_PER_EXCHANGE {
handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
let commit1_retry = expect_commit(&mut sink);
expect_reset_timeout(&mut sink, Timeout::Retransmission);
assert_eq!(commit1, commit1_retry);
}
handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
}
#[test]
fn abort_confirm_after_too_many_timeouts() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
let mut sink = vec![];
for i in 0..MAX_RETRIES_PER_EXCHANGE {
handshake.sta2.handle_timeout(&mut sink, Timeout::Retransmission);
let confirm2_retry = expect_confirm(&mut sink);
expect_reset_timeout(&mut sink, Timeout::Retransmission);
assert_eq!(
confirm2.to_rx().msg().send_confirm + i + 1,
confirm2_retry.to_rx().msg().send_confirm
);
}
handshake.sta2.handle_timeout(&mut sink, Timeout::Retransmission);
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
}
#[test]
fn ignore_unexpected_retransmit_timeout() {
let mut handshake = TestHandshake::new();
let mut sink = vec![];
handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
assert!(sink.is_empty());
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
assert!(sink.is_empty());
}
#[test]
fn fail_on_early_key_expiration() {
let mut handshake = TestHandshake::new();
handshake.sta1_init();
let mut sink = vec![];
handshake.sta1.handle_timeout(&mut sink, Timeout::KeyExpiration);
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::InternalError(_)));
}
#[test]
fn key_expiration_timeout() {
let mut handshake = TestHandshake::new();
let commit1 = handshake.sta1_init();
let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
let mut sink = vec![];
handshake.sta1.handle_timeout(&mut sink, Timeout::KeyExpiration);
assert_eq!(sink.len(), 1);
assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::KeyExpiration));
}
}