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}