Skip to content

fix(digstore-remote): owner-only Windows ACL + zeroize transient identity seed buffer - #41

Merged
MichaelTaylor3d merged 5 commits into
mainfrom
claude/identity-seed-at-rest-hardening
Aug 11, 2026
Merged

fix(digstore-remote): owner-only Windows ACL + zeroize transient identity seed buffer#41
MichaelTaylor3d merged 5 commits into
mainfrom
claude/identity-seed-at-rest-hardening

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

What + why

The persistent USER-identity seed (identity_key.bin, crates/digstore-remote/src/identity.rs) names the operator in every dig://<user>@host origin — exfiltrating it impersonates the user. Two at-rest weaknesses (dig_ecosystem #2168, the separable riders the issue body flags as landable first) are closed here:

  1. Windows ACL parity. write_secret_file's #[cfg(not(unix))] branch was a bare std::fs::write with no explicit ACL — it only inherited the profile-dir ACL ("inheritance, not design"), so a redirected/roaming %APPDATA% or a loosened profile silently widened read access. Now, on Windows, the seed file gets an explicit owner-only, inheritance-blocking (PROTECTED) DACL via SetNamedSecurityInfoW(DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION) — a single ACE granting FILE_ALL_ACCESS to the current-user SID and no one else — the effective parity with the Unix 0o600 path (which is unchanged). RAII guards free the process token and the allocated ACL.
  2. Zeroize transient seed material. The disk-read buffer in load_or_create_seed is now zeroize::Zeroizing, scrubbed on drop, so transient plaintext seed copies don't linger.

Scope (STRICT)

Riders only. The #2168 boundary fix (give dig-node a distinct machine keypair separate from the user identity, + a migration for provisioned nodes) is a decider-deferred cross-repo epic and is NOT touched here — peer-identity / machine-key / peer_id derivation is untouched, public API signatures unchanged (load_or_create_seed still returns [u8;32]). The by-design seed Copy captured into the long-lived signer closures (to re-derive the BLS key per call) is left as-is — that lingering copy is the epic's concern, not this rider. #2168 stays OPEN after this merges.

How verified

  • TDD, RED→GREEN by commit: 8101064 = failing #[cfg(windows)] DACL test only; 3d67fe5 = fix. The test asserts two independent properties so a bare inherited-ACL write fails deterministically regardless of the CI tempdir's inherited ACEs: (1) the DACL is SE_DACL_PROTECTED (an inherited/default DACL is not — the load-bearing distinguisher), and (2) neither Everyone (S-1-1-0) nor BUILTIN\Users has an ACE. digs CI runs cargo nextest run --workspace on windows-latest, so this gates.
  • Local (Linux): cargo test -p digstore-remote green (the stability test exercises the Zeroizing disk-read path); cargo fmt --check clean; cargo clippy --all-targets zero warnings.

Blast radius

Confined to crates/digstore-remote/src/identity.rs + its Cargo.toml (added zeroize = "1" matching the graph's 1.9.0; windows-sys = "0.59" under [target.'cfg(windows)'.dependencies], matching the 0.59.0 already in the lockfile — no second version line). No caller signature changed. FFI symbols/structs verified against the local windows-sys 0.59.0 source.

Version

Root workspace Cargo.toml 0.23.10.23.2 (the file ensure-version-increment.yml diffs). Patch — compatible defense-in-depth hardening, no API/behavior change on the read/sign path.

Closes part of #2168 (riders only; issue remains open for the boundary epic).


Generated by Claude Code

claude added 3 commits August 11, 2026 04:04
Co-Authored-By: Claude <noreply@anthropic.com>
…DACL on Windows

RED proof for the Windows ACL-parity rider. The test is #[cfg(windows)] so it
runs only on the windows-latest CI job; it asserts the persisted identity seed
file carries an explicit PROTECTED (inheritance-blocking) owner-only DACL and
that neither Everyone nor BUILTIN\Users has an ACE. Without the follow-up fix,
write_secret_file does a bare std::fs::write whose inherited DACL is NOT
protected, so this fails deterministically.

Co-Authored-By: Claude <noreply@anthropic.com>
…ntity seed buffer

Two at-rest hardening riders on the persistent user identity seed (identity_key.bin).

1. Windows ACL parity. write_secret_file previously did a bare std::fs::write on
   non-Unix, so the seed only INHERITED the profile-dir ACL. A redirected/roaming
   %APPDATA% or a loosened profile could silently widen read access. It now stamps
   an EXPLICIT, PROTECTED (inheritance-blocking) DACL granting only the current
   user (SetNamedSecurityInfoW + SetEntriesInAclW), the effective parity with the
   Unix 0o600 mode. Unix branch unchanged.

2. Zeroize transient seed material. The plaintext buffer read from disk in
   load_or_create_seed is wrapped in zeroize::Zeroizing so it is scrubbed on drop.
   The returned [u8;32] Copy that signer closures keep alive for the process
   lifetime is by-design and out of scope (the deferred boundary epic).

Peer-identity / machine-key / peer_id derivation untouched. Public API unchanged.
Bumps workspace version 0.23.1 -> 0.23.2 (patch; compatible hardening).

Closes riders 1+2 of dig_ecosystem #2168.

Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d MichaelTaylor3d changed the title fix(digstore-remote): harden identity seed at rest — Windows owner-only ACL + zeroize transient buffers fix(digstore-remote): owner-only Windows ACL + zeroize transient identity seed buffer Aug 11, 2026
claude added 2 commits August 11, 2026 07:51
…ippy mixed-attributes)

The mod carried both an outer /// summary and inner //! docs, tripping clippy::mixed_attributes_style (-D warnings) on the windows-latest build. Fold the summary into the inner //! block; no code change.

Co-Authored-By: Claude <noreply@anthropic.com>
…lippy)

psd is PSECURITY_DESCRIPTOR (*mut c_void); casting it to *mut c_void tripped clippy::unnecessary_cast (-D warnings) on the windows-latest build. Pass psd directly to LocalFree.

Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d merged commit d2f5c12 into main Aug 11, 2026
10 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the claude/identity-seed-at-rest-hardening branch August 11, 2026 08:26
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