Game control endpoint: draw its token from the OS random generator

The TCP control endpoint's token (it gates full control of the running
game) was two SipHash outputs of RandomState over the time and the process
ID. RandomState's keys are OS-seeded, but std doesn't promise it is a
cryptographic generator. It now comes from getrandom::fill, as
horizon-server's and the relay's tokens do (getrandom 0.3, already in the
lockfile, added to horizon-client-lib, which compiles control.rs).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stH5q6PgMAVUSmJqKDe
This commit is contained in:
yahya-nusseibehandClaude Opus 5.5 committed 2026-11-04 14:16:00 +02:00
1 parent 9cb9d76054
commit aa89153fc0
3 files changed
+8 -15

No files matched your search

Generated
+1
View File
@@ -1400,6 +1400,7 @@ dependencies = [
"chrono",
"flate2",
"fs4",
"getrandom 0.3.4",
"gilrs",
"glam",
"horizon-anticheat",
+2
View File
@@ -30,6 +30,8 @@ horizon-world = { path = "../horizon-world" }
horizon-protocol = { path = "../horizon-protocol" }
# server.properties, eula.txt and the user lists, shared with horizon-server.
horizon-server-files = { path = "../horizon-server-files" }
# The control endpoint's token (control.rs), as horizon-server's and the relay's.
getrandom = "0.3"
# The dedicated server's anticheat, off unless server.toml's [anticheat] turns it on (docs/anticheat.md).
horizon-anticheat = { path = "../horizon-anticheat" }
# The dedicated server's chat filter: [chat_filter], the slur list, mutes (docs/chat-filter.md).
+5 -15
View File
@@ -114,22 +114,12 @@ pub fn connect(path: &Path) -> io::Result<Stream> {
Ok(Stream::Tcp(stream))
}
/// 128 random bits as hex, from the standard library's OS-seeded hasher keys.
/// 128 random bits from the OS's random generator, as 32 hex digits: the TCP endpoint's token, which
/// gates full control of the running game.
fn random_token() -> String {
use std::hash::{BuildHasher, Hasher};
(0..2)
.map(|i| {
let mut h = std::collections::hash_map::RandomState::new().build_hasher();
h.write_u64(i);
h.write_u128(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_nanos()),
);
h.write_u32(std::process::id());
format!("{:016x}", h.finish())
})
.collect()
let mut bytes = [0u8; 16];
getrandom::fill(&mut bytes).expect("OS random generator");
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
/// Writes the endpoint file so that only this user can read it, even over an older file