diff --git a/src/wallet/seed.rs b/src/wallet/seed.rs index 509134a7..002f2d84 100644 --- a/src/wallet/seed.rs +++ b/src/wallet/seed.rs @@ -18,7 +18,6 @@ use grin_wallet_impls::Error; use rand::{Rng, rng}; use serde_derive::{Deserialize, Serialize}; use serde_json; -use std::fs::File; use std::io::Write; use ring::aead; @@ -48,9 +47,32 @@ impl WalletSeed { let seed = WalletSeed::from_mnemonic(recovery_phrase)?; let enc_seed = EncryptedWalletSeed::from_seed(&seed, password)?; let enc_seed_json = serde_json::to_string_pretty(&enc_seed).map_err(|_| Error::Format)?; - let mut file = File::create(seed_file_path).map_err(|_| Error::IO)?; - file.write_all(&enc_seed_json.as_bytes()) - .map_err(|_| Error::IO)?; + // The encrypted seed is the wallet's highest-value secret. Write it + // owner-only (0600) on Unix, mirroring the Nostr nsec (see + // src/nostr/identity.rs write_private), so other local users can't read + // it and attempt an offline password grind. Non-Unix keeps default perms. + #[cfg(unix)] + { + use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; + let mut file = std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .mode(0o600) + .open(seed_file_path) + .map_err(|_| Error::IO)?; + file.write_all(&enc_seed_json.as_bytes()) + .map_err(|_| Error::IO)?; + // Tighten perms if the file already existed with a looser mode. + let _ = + std::fs::set_permissions(seed_file_path, std::fs::Permissions::from_mode(0o600)); + } + #[cfg(not(unix))] + { + let mut file = std::fs::File::create(seed_file_path).map_err(|_| Error::IO)?; + file.write_all(&enc_seed_json.as_bytes()) + .map_err(|_| Error::IO)?; + } Ok(seed) } }