netstack3_tcp/socket/generators.rs
1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Generate TCP parameters securely.
6
7use core::hash::{Hash, Hasher};
8
9use netstack3_base::{Instant, Milliseconds, SeqNum, Timestamp};
10use rand::Rng;
11use siphasher::sip128::SipHasher24;
12
13/// A secure generator of TCP parameters.
14///
15/// This generator is modeled off ISN generation as specified in RFC 6528,
16/// however it applies more broadly to other TCP parameters (e.g. the initial
17/// timestamp used in the timestamp option.)
18#[derive(Default)]
19struct Generator<Instant> {
20 // Secret used to choose secure values. It will be filled by a
21 // CSPRNG upon initialization. RFC suggests an implementation "could"
22 // change the secret key on a regular basis, this is not something we are
23 // considering as Linux doesn't seem to do that either.
24 secret: [u8; 16],
25 // The initial timestamp that will be used to calculate the elapsed time
26 // since the beginning and that information will then be used to generate
27 // secure values being requested.
28 timestamp: Instant,
29}
30
31impl<I: Instant> Generator<I> {
32 pub(crate) fn new(now: I, rng: &mut impl Rng) -> Self {
33 let mut secret = [0; 16];
34 rng.fill(&mut secret[..]);
35 Self { secret, timestamp: now }
36 }
37
38 pub(crate) fn generate<A: Hash, P: Hash>(&self, now: I, local: (A, P), remote: (A, P)) -> u32 {
39 let Self { secret, timestamp } = self;
40
41 // Per RFC 6528 Section 3 (https://tools.ietf.org/html/rfc6528#section-3):
42 //
43 // TCP SHOULD generate its Initial Sequence Numbers with the expression:
44 //
45 // ISN = M + F(localip, localport, remoteip, remoteport, secretkey)
46 //
47 // where M is the 4 microsecond timer, and F() is a pseudorandom
48 // function (PRF) of the connection-id.
49 //
50 // Siphash is used here as it is the hash function used by Linux.
51 let h = {
52 let mut hasher = SipHasher24::new_with_key(secret);
53 local.hash(&mut hasher);
54 remote.hash(&mut hasher);
55 hasher.finish()
56 };
57
58 // Reduce the hashed output (h: u64) to 32 bits using XOR, but also
59 // preserve entropy.
60 let elapsed = now.saturating_duration_since(*timestamp);
61 ((elapsed.as_micros() / 4) as u32).wrapping_add(h as u32 ^ (h >> 32) as u32)
62 }
63}
64
65/// A generator of Initial Sequence Numbers, as specified in RFC 6528.
66#[derive(Default)]
67pub struct IsnGenerator<Instant> {
68 inner: Generator<Instant>,
69}
70
71impl<I: Instant> IsnGenerator<I> {
72 pub(crate) fn new(now: I, rng: &mut impl Rng) -> Self {
73 Self { inner: Generator::new(now, rng) }
74 }
75
76 pub(crate) fn generate<A: Hash, P: Hash>(
77 &self,
78 now: I,
79 local: (A, P),
80 remote: (A, P),
81 ) -> SeqNum {
82 SeqNum::new(self.inner.generate(now, local, remote))
83 }
84}
85
86/// A generator of offsets for the timestamp option, as specified in RFC 7323.
87#[derive(Default)]
88pub struct TimestampOffsetGenerator<Instant> {
89 inner: Generator<Instant>,
90}
91
92impl<I: Instant> TimestampOffsetGenerator<I> {
93 pub(crate) fn new(now: I, rng: &mut impl Rng) -> Self {
94 Self { inner: Generator::new(now, rng) }
95 }
96
97 pub(crate) fn generate<A: Hash, P: Hash>(
98 &self,
99 now: I,
100 local: (A, P),
101 remote: (A, P),
102 ) -> Timestamp<Milliseconds> {
103 // Per RFC 7323, section 5.4:
104 // A random offset may be added to the timestamp clock on a per-
105 // connection basis. See [RFC6528], Section 3, on randomizing the
106 // initial sequence number (ISN). The same function with a different
107 // secret key can be used to generate the per-connection timestamp
108 // offset.
109 Timestamp::new(self.inner.generate(now, local, remote))
110 }
111}