use crate::QsslError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionRole {
Client,
Server,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HandshakeState {
Init,
ClientHello,
ServerHello,
Certificate,
KeyExchange,
CertificateVerify,
Finished,
Established,
Closed,
}
impl HandshakeState {
pub fn can_transition_to(&self, next: HandshakeState, role: ConnectionRole) -> bool {
use HandshakeState::*;
match (self, next, role) {
(Init, ClientHello, ConnectionRole::Client) => true,
(ClientHello, ServerHello, ConnectionRole::Client) => true,
(ServerHello, Certificate, ConnectionRole::Client) => true,
(Certificate, KeyExchange, ConnectionRole::Client) => true,
(KeyExchange, CertificateVerify, ConnectionRole::Client) => true,
(CertificateVerify, Finished, ConnectionRole::Client) => true,
(Finished, Established, ConnectionRole::Client) => true,
(Init, ClientHello, ConnectionRole::Server) => true,
(ClientHello, ServerHello, ConnectionRole::Server) => true,
(ServerHello, Certificate, ConnectionRole::Server) => true,
(Certificate, KeyExchange, ConnectionRole::Server) => true,
(KeyExchange, CertificateVerify, ConnectionRole::Server) => true,
(CertificateVerify, Finished, ConnectionRole::Server) => true,
(Finished, Established, ConnectionRole::Server) => true,
(_, Closed, _) => true,
_ => false,
}
}
pub fn transition_to(
&mut self,
next: HandshakeState,
role: ConnectionRole,
) -> Result<(), QsslError> {
if self.can_transition_to(next, role) {
log::debug!("State transition: {:?} -> {:?}", self, next);
*self = next;
Ok(())
} else {
Err(QsslError::InvalidStateTransition {
from: *self,
to: next,
})
}
}
pub fn is_complete(&self) -> bool {
matches!(self, HandshakeState::Established)
}
pub fn is_closed(&self) -> bool {
matches!(self, HandshakeState::Closed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_client_transitions() {
let mut state = HandshakeState::Init;
let role = ConnectionRole::Client;
assert!(state.transition_to(HandshakeState::ClientHello, role).is_ok());
assert!(state.transition_to(HandshakeState::ServerHello, role).is_ok());
assert!(state.transition_to(HandshakeState::Certificate, role).is_ok());
assert!(state.transition_to(HandshakeState::KeyExchange, role).is_ok());
assert!(state.transition_to(HandshakeState::CertificateVerify, role).is_ok());
assert!(state.transition_to(HandshakeState::Finished, role).is_ok());
assert!(state.transition_to(HandshakeState::Established, role).is_ok());
assert!(state.is_complete());
}
#[test]
fn test_invalid_transition() {
let mut state = HandshakeState::Init;
let role = ConnectionRole::Client;
assert!(state.transition_to(HandshakeState::Established, role).is_err());
}
}