snow/
stateless_transportstate.rs

1use crate::{
2    cipherstate::StatelessCipherStates,
3    constants::{MAXDHLEN, MAXMSGLEN, TAGLEN},
4    error::{Error, StateProblem},
5    handshakestate::HandshakeState,
6    params::HandshakePattern,
7    utils::Toggle,
8};
9use std::{convert::TryFrom, fmt};
10
11/// A state machine encompassing the transport phase of a Noise session, using the two
12/// `CipherState`s (for sending and receiving) that were spawned from the `SymmetricState`'s
13/// `Split()` method, called after a handshake has been finished.
14///
15/// See: https://noiseprotocol.org/noise.html#the-handshakestate-object
16pub struct StatelessTransportState {
17    cipherstates: StatelessCipherStates,
18    pattern:      HandshakePattern,
19    dh_len:       usize,
20    rs:           Toggle<[u8; MAXDHLEN]>,
21    initiator:    bool,
22}
23
24impl StatelessTransportState {
25    pub(crate) fn new(handshake: HandshakeState) -> Result<Self, Error> {
26        if !handshake.is_handshake_finished() {
27            return Err(StateProblem::HandshakeNotFinished.into());
28        }
29
30        let dh_len = handshake.dh_len();
31        let HandshakeState { cipherstates, params, rs, initiator, .. } = handshake;
32        let pattern = params.handshake.pattern;
33
34        Ok(Self { cipherstates: cipherstates.into(), pattern, dh_len, rs, initiator })
35    }
36
37    /// Get the remote party's static public key, if available.
38    ///
39    /// Note: will return `None` if either the chosen Noise pattern
40    /// doesn't necessitate a remote static key, *or* if the remote
41    /// static key is not yet known (as can be the case in the `XX`
42    /// pattern, for example).
43    pub fn get_remote_static(&self) -> Option<&[u8]> {
44        self.rs.get().map(|rs| &rs[..self.dh_len])
45    }
46
47    /// Construct a message from `payload` (and pending handshake tokens if in handshake state),
48    /// and writes it to the `output` buffer.
49    ///
50    /// Returns the size of the written payload.
51    ///
52    /// # Errors
53    ///
54    /// Will result in `Error::Input` if the size of the output exceeds the max message
55    /// length in the Noise Protocol (65535 bytes).
56    pub fn write_message(
57        &self,
58        nonce: u64,
59        payload: &[u8],
60        message: &mut [u8],
61    ) -> Result<usize, Error> {
62        if !self.initiator && self.pattern.is_oneway() {
63            return Err(StateProblem::OneWay.into());
64        } else if payload.len() + TAGLEN > MAXMSGLEN || payload.len() + TAGLEN > message.len() {
65            return Err(Error::Input);
66        }
67
68        let cipher = if self.initiator { &self.cipherstates.0 } else { &self.cipherstates.1 };
69        cipher.encrypt(nonce, payload, message)
70    }
71
72    /// Reads a noise message from `input`
73    ///
74    /// Returns the size of the payload written to `payload`.
75    ///
76    /// # Errors
77    ///
78    /// Will result in `Error::Decrypt` if the contents couldn't be decrypted and/or the
79    /// authentication tag didn't verify.
80    ///
81    /// Will result in `StateProblem::Exhausted` if the max nonce overflows.
82    pub fn read_message(
83        &self,
84        nonce: u64,
85        payload: &[u8],
86        message: &mut [u8],
87    ) -> Result<usize, Error> {
88        if payload.len() > MAXMSGLEN {
89            Err(Error::Input)
90        } else if self.initiator && self.pattern.is_oneway() {
91            Err(StateProblem::OneWay.into())
92        } else {
93            let cipher = if self.initiator { &self.cipherstates.1 } else { &self.cipherstates.0 };
94            cipher.decrypt(nonce, payload, message)
95        }
96    }
97
98    /// Generates a new key for the egress symmetric cipher according to Section 4.2
99    /// of the Noise Specification. Synchronizing timing of rekey between initiator and
100    /// responder is the responsibility of the application, as described in Section 11.3
101    /// of the Noise Specification.
102    pub fn rekey_outgoing(&mut self) {
103        if self.initiator {
104            self.cipherstates.rekey_initiator()
105        } else {
106            self.cipherstates.rekey_responder()
107        }
108    }
109
110    /// Generates a new key for the ingress symmetric cipher according to Section 4.2
111    /// of the Noise Specification. Synchronizing timing of rekey between initiator and
112    /// responder is the responsibility of the application, as described in Section 11.3
113    /// of the Noise Specification.
114    pub fn rekey_incoming(&mut self) {
115        if self.initiator {
116            self.cipherstates.rekey_responder()
117        } else {
118            self.cipherstates.rekey_initiator()
119        }
120    }
121
122    /// Set a new key for the one or both of the initiator-egress and responder-egress symmetric ciphers.
123    pub fn rekey_manually(&mut self, initiator: Option<&[u8]>, responder: Option<&[u8]>) {
124        if let Some(key) = initiator {
125            self.rekey_initiator_manually(key);
126        }
127        if let Some(key) = responder {
128            self.rekey_responder_manually(key);
129        }
130    }
131
132    /// Set a new key for the initiator-egress symmetric cipher.
133    pub fn rekey_initiator_manually(&mut self, key: &[u8]) {
134        self.cipherstates.rekey_initiator_manually(key)
135    }
136
137    /// Set a new key for the responder-egress symmetric cipher.
138    pub fn rekey_responder_manually(&mut self, key: &[u8]) {
139        self.cipherstates.rekey_responder_manually(key)
140    }
141
142    /// Check if this session was started with the "initiator" role.
143    pub fn is_initiator(&self) -> bool {
144        self.initiator
145    }
146}
147
148impl fmt::Debug for StatelessTransportState {
149    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
150        fmt.debug_struct("StatelessTransportState").finish()
151    }
152}
153
154impl TryFrom<HandshakeState> for StatelessTransportState {
155    type Error = Error;
156
157    fn try_from(old: HandshakeState) -> Result<Self, Self::Error> {
158        StatelessTransportState::new(old)
159    }
160}