From 4baba58dc392601109e90f5d6b7fcc1278ebc10e Mon Sep 17 00:00:00 2001 From: 2ro <17595647+2ro@users.noreply.github.com> Date: Fri, 10 Jul 2026 13:41:43 -0400 Subject: [PATCH] Write encrypted money-seed file with 0600 permissions on Unix WalletSeed::init_file now creates wallet.seed owner-only (0600) via OpenOptionsExt, mirroring the Nostr nsec in src/nostr/identity.rs, so other local users cannot read it for an offline password grind. Uses #[cfg(unix)] so non-Unix still compiles. KDF unchanged. --- src/wallet/seed.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) 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) } }