fix: Phase 7 — key zeroization, OsRng, checked arithmetic, TOTP rate limits
- SecretsManager: raw key stored in Zeroizing<[u8; 32]>, auto-zeroed on drop - SecretsManager: replaced thread_rng with OsRng (CSPRNG) for nonces - Remember-me secret: derived from machine-id via SHA-256 (deterministic, no plaintext key storage) - Bitcoin ecash balance: uses checked_add with u64::MAX saturation on overflow - TOTP setup/confirm: added to EndpointRateLimiter (3 and 5 per 5min) - AppId validation and Tor service name validation already existed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,7 @@ pub struct ExpiringSecret {
|
||||
pub struct SecretsManager {
|
||||
secrets_dir: PathBuf,
|
||||
cipher: Aes256Gcm,
|
||||
raw_key: zeroize::Zeroizing<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl SecretsManager {
|
||||
@@ -49,11 +50,14 @@ impl SecretsManager {
|
||||
"Encryption key must be exactly 32 bytes (256 bits), got {}",
|
||||
encryption_key.len()
|
||||
);
|
||||
let cipher = Aes256Gcm::new_from_slice(&encryption_key)
|
||||
let mut key_array = [0u8; 32];
|
||||
key_array.copy_from_slice(&encryption_key);
|
||||
let cipher = Aes256Gcm::new_from_slice(&key_array)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to create cipher: {}", e))?;
|
||||
Ok(Self {
|
||||
secrets_dir,
|
||||
cipher,
|
||||
raw_key: zeroize::Zeroizing::new(key_array),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,7 +65,7 @@ impl SecretsManager {
|
||||
/// Returns: MAGIC (10 bytes) + nonce (12 bytes) + ciphertext (variable)
|
||||
fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
|
||||
let mut nonce_bytes = [0u8; 12];
|
||||
rand::thread_rng().fill_bytes(&mut nonce_bytes);
|
||||
rand::rngs::OsRng.fill_bytes(&mut nonce_bytes);
|
||||
let nonce = Nonce::from_slice(&nonce_bytes);
|
||||
|
||||
let ciphertext = self
|
||||
@@ -220,7 +224,7 @@ impl SecretsManager {
|
||||
) -> Result<String> {
|
||||
// Generate a new random secret (32 bytes, hex-encoded = 64 chars)
|
||||
let mut new_secret_bytes = [0u8; 32];
|
||||
rand::thread_rng().fill_bytes(&mut new_secret_bytes);
|
||||
rand::rngs::OsRng.fill_bytes(&mut new_secret_bytes);
|
||||
let new_value = hex::encode(new_secret_bytes);
|
||||
|
||||
let secret_path = self
|
||||
|
||||
Reference in New Issue
Block a user