mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-09-02 17:49:20 +00:00
fix(security): use OsRng for all security-critical key and token generation (#519)
* fix(security): use OsRng for all security-critical key and token generation Replace rand::thread_rng() with rand::rngs::OsRng in all security-critical code paths that generate cryptographic key material, bearer tokens, PKCE verifiers, CSRF state parameters, and webhook secrets. thread_rng() uses a userspace CSPRNG (ChaCha) seeded from OS entropy, which is fine for non-security contexts but adds an unnecessary intermediate layer for key material where direct OS entropy (OsRng) is the correct choice. Files changed: - src/secrets/keychain.rs: master encryption key generation - src/secrets/crypto.rs: per-secret HKDF salt generation - src/orchestrator/auth.rs: per-job bearer token generation - src/channels/web/mod.rs: gateway auth token fallback - src/cli/oauth_defaults.rs: OAuth PKCE verifier and CSRF state - src/tools/mcp/auth.rs: MCP OAuth PKCE verifier - src/extensions/manager.rs: auto-generated extension secrets - src/setup/channels.rs: webhook secret generation Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix(security): address PR review feedback for OsRng migration - Remove shadowing inner `use rand::rngs::OsRng` in `generate_salt()`; use module-level `aes_gcm::aead::OsRng` import instead (same type, avoids divergence risk if rand_core versions drift) - Fix missed callsites in `pairing/store.rs`: `random_code()` and `generate_unique_code()` now use `OsRng` for pairing auth codes - Add regression tests for `generate_salt()`: correct length, non-zero output, uniqueness across calls Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
df49b17d0f
commit
6a2a6cd050
+20
-1
@@ -59,7 +59,7 @@ impl SecretsCrypto {
|
||||
/// Generate a random salt for a new secret.
|
||||
pub fn generate_salt() -> Vec<u8> {
|
||||
let mut salt = vec![0u8; SALT_SIZE];
|
||||
rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut salt);
|
||||
rand::RngCore::fill_bytes(&mut OsRng, &mut salt);
|
||||
salt
|
||||
}
|
||||
|
||||
@@ -247,4 +247,23 @@ mod tests {
|
||||
let decrypted = crypto.decrypt(&encrypted, &salt).unwrap();
|
||||
assert_eq!(decrypted.expose().as_bytes(), plaintext.as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_salt_correct_length() {
|
||||
let salt = SecretsCrypto::generate_salt();
|
||||
assert_eq!(salt.len(), super::SALT_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_salt_nonzero() {
|
||||
let salt = SecretsCrypto::generate_salt();
|
||||
assert!(salt.iter().any(|&b| b != 0), "salt should not be all zeros");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_salt_unique() {
|
||||
let s1 = SecretsCrypto::generate_salt();
|
||||
let s2 = SecretsCrypto::generate_salt();
|
||||
assert_ne!(s1, s2, "two generated salts should not be identical");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,9 @@ const MASTER_KEY_ACCOUNT: &str = "master_key";
|
||||
/// Generate a random 32-byte master key.
|
||||
pub fn generate_master_key() -> Vec<u8> {
|
||||
use rand::RngCore;
|
||||
use rand::rngs::OsRng;
|
||||
let mut key = vec![0u8; 32];
|
||||
rand::thread_rng().fill_bytes(&mut key);
|
||||
OsRng.fill_bytes(&mut key);
|
||||
key
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user