diff --git a/Cargo.lock b/Cargo.lock index 6df10c6..b04ef90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4843,6 +4843,7 @@ dependencies = [ "pdf-extract", "probe-rs", "prometheus", + "rand 0.8.5", "reqwest", "rppal", "rusqlite", diff --git a/Cargo.toml b/Cargo.toml index a9ff034..2c314c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,9 @@ hmac = "0.12" sha2 = "0.10" hex = "0.4" +# CSPRNG for secure token generation +rand = "0.8" + # Landlock (Linux sandbox) - optional dependency landlock = { version = "0.4", optional = true } diff --git a/src/security/pairing.rs b/src/security/pairing.rs index c0ce018..18177a3 100644 --- a/src/security/pairing.rs +++ b/src/security/pairing.rs @@ -201,9 +201,17 @@ fn generate_code() -> String { } } -/// Generate a cryptographically-adequate bearer token (hex-encoded). +/// Generate a cryptographically-adequate bearer token with 256-bit entropy. +/// +/// Uses `rand::thread_rng()` which is backed by the OS CSPRNG +/// (/dev/urandom on Linux, BCryptGenRandom on Windows, SecRandomCopyBytes +/// on macOS). The 32 random bytes (256 bits) are hex-encoded for a +/// 64-character token, providing 256 bits of entropy. fn generate_token() -> String { - format!("zc_{}", uuid::Uuid::new_v4().as_simple()) + use rand::RngCore; + let mut bytes = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut bytes); + format!("zc_{}", hex::encode(&bytes)) } /// SHA-256 hash a bearer token for storage. Returns lowercase hex.