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
(cherry picked from commit aa89153fc0)
This commit is contained in:
yahya-nusseibeh authored and Horizon agent committed 2026-11-05 04:11:20 +01:00
1 parent a295df1fd7
commit 55345332a3
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()
}
/// Write the endpoint file so that only this user can read it.