Skip to content

fix(keystore): force secret key files to 0600 on every write, not just creation - #2468

Closed
ygd58 wants to merge 1 commit into
0xMiden:nextfrom
ygd58:fix-keystore-file-permissions
Closed

fix(keystore): force secret key files to 0600 on every write, not just creation#2468
ygd58 wants to merge 1 commit into
0xMiden:nextfrom
ygd58:fix-keystore-file-permissions

Conversation

@ygd58

@ygd58 ygd58 commented Aug 29, 2026

Copy link
Copy Markdown

write_secret_key_file opens with OpenOptions::mode(0o600), but on POSIX the mode argument to open() only applies when the file is newly created - it has no effect if a file already exists at that path. Since this function also passes .truncate(true) to overwrite an existing file, a key file that was already present with looser permissions (e.g. restored from a backup, copied without preserving permissions, or written by a client predating #1833's 0600 restriction) keeps those looser permissions after being rewritten here, silently leaving secret key material readable by other local users/processes.

Context

#1833 (merged) is the security-audit PR that introduced mode(0o600) in the first place. This closes the gap it didn't cover - overwriting a pre-existing file - by calling set_permissions(0o600) explicitly on the open file handle after writing, so the file ends up 0600 unconditionally regardless of what permissions it had before this call.

Test plan

Added a regression test: pre-create a file at 0644, call write_secret_key_file, assert it ends up 0600 (this test fails against the pre-fix code, since a 0644 file kept its permissions on overwrite).

Ran locally, not just CI:

cargo test -p miden-client --lib keystore::fs_keystore::tests::overwriting_an_existing_key_file_still_ends_up_0600 -- --nocapture
cargo test -p miden-client --lib

New test: 1 passed. Full lib test suite: 130 passed, 0 failed - no regressions.

…t creation

write_secret_key_file opens with OpenOptions::mode(0o600), but on POSIX
the mode argument to open() only applies when the file is newly created -
it has no effect if a file already exists at that path. Since this
function also passes .truncate(true) to overwrite an existing file, a key
file that was already present with looser permissions (e.g. restored from
a backup, copied without preserving permissions, or written by a client
predating 0xMiden#1833's 0600 restriction) keeps those looser permissions after
being rewritten here, silently leaving secret key material readable by
other local users/processes.

0xMiden#1833 (merged) is the security-audit PR that introduced mode(0o600) in the
first place; this closes the gap it didn't cover (overwriting a
pre-existing file), by calling set_permissions(0o600) explicitly on the
open file handle after writing, so the file ends up 0600 unconditionally
regardless of what permissions it had before this call.

Adds a regression test: pre-create a file at 0644, call
write_secret_key_file, assert it ends up 0600 (this test would fail
against the pre-fix code, since 0644 files kept their permissions on
overwrite).

I don't have a matching Rust toolchain in my current environment, so this
needs local build + test verification before being considered ready, same
as my prior PRs in this repo.
@juan518munoz

Copy link
Copy Markdown
Collaborator

Security issues are to be reported rather than directly addressed

https://github.com/0xMiden/.github/blob/main/SECURITY.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants