Skip to content

fix(core): zeroize the derived X25519 secret (#65)#66

Merged
kevincodex1 merged 1 commit into
fix/zeroize-seed-bytesfrom
fix/zeroize-x25519-secret
Jun 23, 2026
Merged

fix(core): zeroize the derived X25519 secret (#65)#66
kevincodex1 merged 1 commit into
fix/zeroize-seed-bytesfrom
fix/zeroize-x25519-secret

Conversation

@beardthelion

Copy link
Copy Markdown
Collaborator

Stacked on #64 (#41). Based on fix/zeroize-seed-bytes; the diff here is only this change. Retarget/rebase onto main once #64 merges. Please merge #64 first.

Summary

x25519_secret_from_seed derived the X25519 private scalar from the Ed25519 seed and returned it as a bare [u8; 32]. Because [u8; 32] is Copy with no Drop (and sha2's digest output likewise has no zeroize), the scalar, the SHA-512 digest, and the returned temporary were released without being scrubbed. This routes the derived secret through Zeroizing and explicitly wipes the digest.

Motivation & context

Closes #65

Direct sibling of #41/#64: same Copy-no-Drop memory-hygiene class, on the derived X25519 secret rather than the raw seed. #41 was deliberately scoped to the seed accessor and flagged this downstream leak for its own follow-up. The derived scalar is the actual private key fed into crypto_box::SecretKey in open_blob, which runs on every withheld-blob read.

Kind of change

  • Bug fix
  • Feature
  • Security fix
  • Docs
  • Tests / CI
  • Refactor (no behavior change)
  • Breaking or protocol change (issue required first)

What changed

Crate touched: gitlawb-core.

  • x25519_secret_from_seed now returns Zeroizing<[u8; 32]> and builds the scalar directly into a zeroizing buffer, so no bare [u8; 32] secret local persists.
  • The intermediate SHA-512 digest is explicitly wiped (h.as_mut_slice().zeroize(), a volatile non-elidable write) before it drops.
  • The two callers (open_blob and a crypto test) deref the result into crypto_box::SecretKey, which already scrubs its own copy.

No behavior change: the derived scalar and all decryption output are byte-identical, covered by the existing ed25519_to_x25519_keypair_agrees and seal_open_round_trip_for_recipients tests.

How a reviewer can verify

cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings

Before you request review

  • Scope is one logical change; no unrelated churn
  • cargo test --workspace passes locally
  • New behavior is covered by tests (behavior is byte-identical; existing crypto round-trip tests guard it)
  • cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings are clean
  • Commit titles use Conventional Commits (feat(...), fix(...), docs(...))
  • Docs / .env.example updated if behavior or config changed (or N/A)
  • Checked existing PRs so this isn't a duplicate

Protocol & signing impact

  • Touches DID / did:key, Ed25519 / RFC 9421 signatures, UCAN, ref certs, or P2P wire formats
  • Discussed in an issue before implementation
  • Backward-compatible with existing nodes and previously signed history

In-memory hygiene only. No change to the encryption scheme, key derivation math, or wire format; output is byte-identical.

x25519_secret_from_seed derived the X25519 private scalar from the Ed25519
seed and returned it as a bare [u8; 32]. Because [u8; 32] is Copy with no
Drop (and sha2's digest output likewise has no zeroize), the scalar, the
SHA-512 digest, and the returned temporary were released without being
scrubbed, leaving secret-derived material in freed memory. This is the same
Copy-no-Drop class fixed for the raw seed in #41, on the derived secret.

Return Zeroizing<[u8; 32]>, build the scalar directly into a zeroizing
buffer so no bare secret local persists, and explicitly wipe the SHA-512
digest before it drops. Callers deref the result into crypto_box::SecretKey,
which already scrubs its own copy. No behavior change: the derived scalar and
all decryption output are byte-identical, covered by the existing
ed25519_to_x25519_keypair_agrees and seal_open_round_trip_for_recipients tests.

Stacked on #41 (#64).
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0792a9ba-f99b-4367-a12d-181f5d4f2bf1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/zeroize-x25519-secret

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@beardthelion beardthelion added kind:security Vulnerability fix or hardening crate:core gitlawb-core — identity, certs, encrypt, DID/UCAN subsystem:encryption Encrypted subtrees, recipient blinding, key zeroization sev:high Major break or real security/trust risk, no easy workaround labels Jun 22, 2026

@kevincodex1 kevincodex1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kevincodex1 kevincodex1 merged commit 9a46e8c into fix/zeroize-seed-bytes Jun 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crate:core gitlawb-core — identity, certs, encrypt, DID/UCAN kind:security Vulnerability fix or hardening sev:high Major break or real security/trust risk, no easy workaround subsystem:encryption Encrypted subtrees, recipient blinding, key zeroization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants