Skip to content

Commit c6657a7

Browse files
committed
TransportState: limit read_message size to 65535
While not really a big issue since applications should be checking message sizes over the wire as well, this could theoretically allow for DoS's by asking snow to decrypt messages larger than the Noise Protocol specification allows.
1 parent da0ba4d commit c6657a7

File tree

3 files changed

+60
-17
lines changed

3 files changed

+60
-17
lines changed

src/stateless_transportstate.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ impl StatelessTransportState {
4040
/// doesn't necessitate a remote static key, *or* if the remote
4141
/// static key is not yet known (as can be the case in the `XX`
4242
/// pattern, for example).
43-
#[must_use] pub fn get_remote_static(&self) -> Option<&[u8]> {
43+
#[must_use]
44+
pub fn get_remote_static(&self) -> Option<&[u8]> {
4445
self.rs.get().map(|rs| &rs[..self.dh_len])
4546
}
4647

@@ -74,6 +75,7 @@ impl StatelessTransportState {
7475
/// Returns the number of bytes written to `payload`.
7576
///
7677
/// # Errors
78+
/// Will result in `Error::Input` if the message is more than 65535 bytes.
7779
///
7880
/// Will result in `Error::Decrypt` if the contents couldn't be decrypted and/or the
7981
/// authentication tag didn't verify.
@@ -85,11 +87,14 @@ impl StatelessTransportState {
8587
payload: &[u8],
8688
message: &mut [u8],
8789
) -> Result<usize, Error> {
88-
if self.initiator && self.pattern.is_oneway() {
89-
return Err(StateProblem::OneWay.into());
90+
if payload.len() > MAXMSGLEN {
91+
Err(Error::Input)
92+
} else if self.initiator && self.pattern.is_oneway() {
93+
Err(StateProblem::OneWay.into())
94+
} else {
95+
let cipher = if self.initiator { &self.cipherstates.1 } else { &self.cipherstates.0 };
96+
cipher.decrypt(nonce, payload, message)
9097
}
91-
let cipher = if self.initiator { &self.cipherstates.1 } else { &self.cipherstates.0 };
92-
cipher.decrypt(nonce, payload, message)
9398
}
9499

95100
/// Generate a new key for the egress symmetric cipher according to Section 4.2
@@ -141,7 +146,8 @@ impl StatelessTransportState {
141146
}
142147

143148
/// Check if this session was started with the "initiator" role.
144-
#[must_use] pub fn is_initiator(&self) -> bool {
149+
#[must_use]
150+
pub fn is_initiator(&self) -> bool {
145151
self.initiator
146152
}
147153
}

src/transportstate.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ impl TransportState {
4040
/// doesn't necessitate a remote static key, *or* if the remote
4141
/// static key is not yet known (as can be the case in the `XX`
4242
/// pattern, for example).
43-
#[must_use] pub fn get_remote_static(&self) -> Option<&[u8]> {
43+
#[must_use]
44+
pub fn get_remote_static(&self) -> Option<&[u8]> {
4445
self.rs.get().map(|rs| &rs[..self.dh_len])
4546
}
4647

@@ -70,19 +71,22 @@ impl TransportState {
7071
/// Returns the number of bytes written to `payload`.
7172
///
7273
/// # Errors
74+
/// Will result in `Error::Input` if the message is more than 65535 bytes.
7375
///
7476
/// Will result in `Error::Decrypt` if the contents couldn't be decrypted and/or the
7577
/// authentication tag didn't verify.
7678
///
7779
/// Will result in `StateProblem::Exhausted` if the max nonce overflows.
7880
pub fn read_message(&mut self, message: &[u8], payload: &mut [u8]) -> Result<usize, Error> {
79-
if self.initiator && self.pattern.is_oneway() {
80-
return Err(StateProblem::OneWay.into());
81+
if message.len() > MAXMSGLEN {
82+
Err(Error::Input)
83+
} else if self.initiator && self.pattern.is_oneway() {
84+
Err(StateProblem::OneWay.into())
85+
} else {
86+
let cipher =
87+
if self.initiator { &mut self.cipherstates.1 } else { &mut self.cipherstates.0 };
88+
cipher.decrypt(message, payload)
8189
}
82-
let cipher =
83-
if self.initiator { &mut self.cipherstates.1 } else { &mut self.cipherstates.0 };
84-
85-
cipher.decrypt(message, payload)
8690
}
8791

8892
/// Generate a new key for the egress symmetric cipher according to Section 4.2
@@ -147,7 +151,8 @@ impl TransportState {
147151
/// # Errors
148152
///
149153
/// Will result in `Error::State` if not in transport mode.
150-
#[must_use] pub fn receiving_nonce(&self) -> u64 {
154+
#[must_use]
155+
pub fn receiving_nonce(&self) -> u64 {
151156
if self.initiator {
152157
self.cipherstates.1.nonce()
153158
} else {
@@ -160,7 +165,8 @@ impl TransportState {
160165
/// # Errors
161166
///
162167
/// Will result in `Error::State` if not in transport mode.
163-
#[must_use] pub fn sending_nonce(&self) -> u64 {
168+
#[must_use]
169+
pub fn sending_nonce(&self) -> u64 {
164170
if self.initiator {
165171
self.cipherstates.0.nonce()
166172
} else {
@@ -169,7 +175,8 @@ impl TransportState {
169175
}
170176

171177
/// Check if this session was started with the "initiator" role.
172-
#[must_use] pub fn is_initiator(&self) -> bool {
178+
#[must_use]
179+
pub fn is_initiator(&self) -> bool {
173180
self.initiator
174181
}
175182
}

tests/general.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use hex::FromHex;
66
use snow::{
77
resolvers::{CryptoResolver, DefaultResolver},
8-
Builder,
8+
Builder, Error,
99
};
1010

1111
use rand_core::{impls, CryptoRng, RngCore};
@@ -514,6 +514,36 @@ fn test_handshake_message_undersized_output_buffer() -> TestResult {
514514
Ok(())
515515
}
516516

517+
#[test]
518+
fn test_handshake_message_receive_oversized_message() -> TestResult {
519+
let params: NoiseParams = "Noise_NN_25519_ChaChaPoly_SHA256".parse()?;
520+
let mut h_i = Builder::new(params.clone()).build_initiator()?;
521+
let mut h_r = Builder::new(params).build_responder()?;
522+
523+
let mut buffer_msg = [0u8; 100_000];
524+
let mut buffer_out = [0u8; 100_000];
525+
let len = h_i.write_message(b"abc", &mut buffer_msg)?;
526+
assert_eq!(Error::Input, h_r.read_message(&buffer_msg, &mut buffer_out).unwrap_err());
527+
h_r.read_message(&buffer_msg[..len], &mut buffer_out)?;
528+
529+
let len = h_r.write_message(b"defg", &mut buffer_msg)?;
530+
h_i.read_message(&buffer_msg[..len], &mut buffer_out)?;
531+
532+
let h_i = h_i.into_stateless_transport_mode()?;
533+
let mut h_r = h_r.into_transport_mode()?;
534+
535+
let len = h_i.write_message(0, b"hack the planet", &mut buffer_msg)?;
536+
assert_eq!(Error::Input, h_r.read_message(&buffer_msg, &mut buffer_out).unwrap_err());
537+
h_r.read_message(&buffer_msg[..len], &mut buffer_out)?;
538+
539+
let len = h_r.write_message(b"hack the planet", &mut buffer_msg)?;
540+
assert_eq!(Error::Input, h_i.read_message(0, &buffer_msg, &mut buffer_out).unwrap_err());
541+
let len = h_i.read_message(0, &buffer_msg[..len], &mut buffer_out)?;
542+
assert_eq!(&buffer_out[..len], b"hack the planet");
543+
544+
Ok(())
545+
}
546+
517547
#[test]
518548
fn test_transport_message_exceeds_max_len() -> TestResult {
519549
let params: NoiseParams = "Noise_N_25519_ChaChaPoly_SHA256".parse()?;

0 commit comments

Comments
 (0)