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}