s2n_quic/provider/stateless_reset_token.rs
1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Provides stateless reset token support for an endpoint
5
6/// A generator for a stateless reset token
7///
8/// [QUIC§21.11](https://www.rfc-editor.org/rfc/rfc9000.html#reset-oracle) highlights a denial of service
9/// attack that is possible if an attacker can cause an endpoint to transmit a valid stateless reset
10/// token for a connection ID of the attacker's choosing. This attack may be mitigated by ensuring the
11/// `generate` implementation only returns a valid (non-random) `Token` if the given `local_connection_id`
12/// does not correspond to any active connection on any endpoint that uses the same static key for
13/// generating stateless reset tokens. This is in accordance with the following requirement:
14///
15/// > More generally, servers MUST NOT generate a stateless reset
16/// > if a connection with the corresponding connection ID could
17/// > be active on any endpoint using the same static key.
18///
19/// This may require coordination between endpoints and/or careful setup of load balancing and
20/// packet routing, as well as ensuring the connection IDs in use are difficult to guess.
21///
22/// Take these factors into consideration before enabling the Stateless Reset
23/// Token Generator. By default, stateless resets are not transmitted by s2n-quic endpoints,
24/// see [stateless_reset_token::Default][`crate::provider::stateless_reset_token::Default`].
25pub use s2n_quic_core::stateless_reset::token::Generator;
26
27pub trait Provider: 'static {
28 type Generator: 'static + Generator;
29 type Error: core::fmt::Display + Send + Sync;
30
31 fn start(self) -> Result<Self::Generator, Self::Error>;
32}
33
34pub use random::Provider as Default;
35
36impl_provider_utils!();
37
38mod random {
39 use core::convert::Infallible;
40 use rand::prelude::*;
41 use s2n_quic_core::{frame::new_connection_id::STATELESS_RESET_TOKEN_LEN, stateless_reset};
42
43 /// Randomly generated stateless reset token.
44 ///
45 /// Since a random stateless reset token will not be recognized by the peer, this default
46 /// stateless token generator does not enable stateless resets to be sent to the peer.
47 ///
48 /// To enable stateless reset functionality, the stateless reset token must
49 /// be generated the same for a given `local_connection_id` before and after loss of state.
50 #[derive(Debug, Default)]
51 pub struct Provider(Generator);
52
53 impl super::Provider for Provider {
54 type Generator = Generator;
55 type Error = Infallible;
56
57 fn start(self) -> Result<Self::Generator, Self::Error> {
58 Ok(self.0)
59 }
60 }
61
62 impl super::TryInto for Generator {
63 type Provider = Provider;
64 type Error = Infallible;
65
66 fn try_into(self) -> Result<Self::Provider, Self::Error> {
67 Ok(Provider(self))
68 }
69 }
70
71 // Randomly generated stateless reset token.
72 #[derive(Debug, Default)]
73 pub struct Generator {}
74
75 impl stateless_reset::token::Generator for Generator {
76 // Since a random stateless reset token will not be recognized by the peer, this generator
77 // is not enabled and no stateless reset packet will be sent to the peer.
78 const ENABLED: bool = false;
79 // This stateless reset token generator produces a random token on each call, and
80 // thus does not enable stateless reset functionality, as the token provided to the
81 // peer with a new connection ID will be different than the token sent in a stateless
82 // reset. To enable stateless reset functionality, the stateless reset token must
83 // be generated the same for a given `local_connection_id` before and after loss of state.
84 fn generate(&mut self, _local_connection_id: &[u8]) -> stateless_reset::Token {
85 let mut token = [0u8; STATELESS_RESET_TOKEN_LEN];
86 rand::rng().fill_bytes(&mut token);
87 token.into()
88 }
89 }
90
91 #[cfg(test)]
92 mod tests {
93 use super::*;
94 use s2n_quic_core::{connection, stateless_reset::token::Generator as _};
95
96 #[test]
97 fn stateless_reset_token_test() {
98 let mut generator = Generator::default();
99 let id = connection::LocalId::try_from_bytes(b"id01").unwrap();
100
101 let token_1 = generator.generate(id.as_bytes());
102 let token_2 = generator.generate(id.as_bytes());
103
104 assert_ne!(token_1, token_2);
105 }
106 }
107}