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}