netstack3_tcp/socket/
isn.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 initial sequence numbers securely.
6
7use core::hash::{Hash, Hasher};
8
9use netstack3_base::{Instant, SeqNum};
10use rand::Rng;
11use siphasher::sip128::SipHasher24;
12
13/// A generator of TCP initial sequence numbers.
14#[derive(Default)]
15pub struct IsnGenerator<Instant> {
16    // Secret used to choose initial sequence numbers. It will be filled by a
17    // CSPRNG upon initialization. RFC suggests an implementation "could"
18    // change the secret key on a regular basis, this is not something we are
19    // considering as Linux doesn't seem to do that either.
20    secret: [u8; 16],
21    // The initial timestamp that will be used to calculate the elapsed time
22    // since the beginning and that information will then be used to generate
23    // ISNs being requested.
24    timestamp: Instant,
25}
26
27impl<I: Instant> IsnGenerator<I> {
28    pub(crate) fn new(now: I, rng: &mut impl Rng) -> Self {
29        let mut secret = [0; 16];
30        rng.fill(&mut secret[..]);
31        Self { secret, timestamp: now }
32    }
33
34    pub(crate) fn generate<A: Hash, P: Hash>(
35        &self,
36        now: I,
37        local: (A, P),
38        remote: (A, P),
39    ) -> SeqNum {
40        let Self { secret, timestamp } = self;
41
42        // Per RFC 6528 Section 3 (https://tools.ietf.org/html/rfc6528#section-3):
43        //
44        // TCP SHOULD generate its Initial Sequence Numbers with the expression:
45        //
46        //   ISN = M + F(localip, localport, remoteip, remoteport, secretkey)
47        //
48        // where M is the 4 microsecond timer, and F() is a pseudorandom
49        // function (PRF) of the connection-id.
50        //
51        // Siphash is used here as it is the hash function used by Linux.
52        let h = {
53            let mut hasher = SipHasher24::new_with_key(secret);
54            local.hash(&mut hasher);
55            remote.hash(&mut hasher);
56            hasher.finish()
57        };
58
59        // Reduce the hashed output (h: u64) to 32 bits using XOR, but also
60        // preserve entropy.
61        let elapsed = now.saturating_duration_since(*timestamp);
62        SeqNum::new(((elapsed.as_micros() / 4) as u32).wrapping_add(h as u32 ^ (h >> 32) as u32))
63    }
64}