Skip to content

feat: GroupKeyHolder for GMS key management#449

Open
moudyellaz wants to merge 37 commits intomainfrom
moudy/feat-group-key-holder
Open

feat: GroupKeyHolder for GMS key management#449
moudyellaz wants to merge 37 commits intomainfrom
moudy/feat-group-key-holder

Conversation

@moudyellaz
Copy link
Copy Markdown
Collaborator

@moudyellaz moudyellaz commented Apr 15, 2026

🎯 Purpose

This PR adds GroupKeyHolder to the wallet for group-owned private PDAs. A group of controllers shares a Group Master Secret (GMS). From it, each controller independently derives identical keys for any PDA the group owns, enabling private multisig and k-of-n patterns over existing PDA-based programs without changing those programs.

⚙️ Approach

key_protocol: GroupKeyHolder (core cryptography)

  • Per-PDA key derivation via SHA256(domain_prefix || gms || pda_seed) producing NSK/NPK/VSK/VPK through the existing derivation chain.
  • epoch counter + ratchet(rotation_salt) forward-hashes the GMS so removed members cannot derive future keys.
  • seal_for(recipient_vpk) / unseal(sealed, own_vsk) encrypts GMS+epoch via ephemeral ECDH + domain-separated KDF + AES-256-GCM for distribution over untrusted channels.
  • Debug impl redacts GMS, raw accessor named dangerous_raw_gms to flag intent.

key_protocol: NSSAUserData storage

  • group_key_holders: BTreeMap<String, GroupKeyHolder> field with #[serde(default)] for backward compatibility.
  • get_group_key_holder / insert_group_key_holder accessors.

wallet: mask-3 transaction construction

  • PrivateGroupPda { group_label, program_id, seed } variant on PrivacyPreservingAccount.
  • group_pda_preparation derives keys from GroupKeyHolder, computes AccountId::for_private_pda, fetches account state, sets mask 3.

🧪 How to Test

RISC0_DEV_MODE=1 cargo test --release -p key_protocol

20 tests covering: derivation determinism, collision resistance, degenerate inputs, pinned end-to-end derivation, serde round-trip, ratchet (epoch advance, key change, salt divergence, forward secrecy), seal/unseal (round-trip, wrong VSK, tampered ciphertext, randomness, too-short input), NSSAUserData storage.

🔗 Dependencies

🔜 Future Work

  • Toy SimpleGovernance program + integration tests exercising the full stack (circuit + wallet) in both public and private execution.
  • Encrypted-at-rest storage for GroupKeyHolder (pre-existing issue, personal keys have the same gap).
  • Init nullifier/nonce AccountId inclusion (Sergio's refactor).

📋 PR Completion Checklist

  • Complete PR description
  • Implement the core functionality
  • Add/update tests
  • Add/update documentation and inline comments

@moudyellaz moudyellaz force-pushed the moudy/feat-group-key-holder branch 2 times, most recently from 12ef35b to 4938373 Compare April 17, 2026 15:02
Base automatically changed from moudy/feat-private-pdas to main April 22, 2026 22:34
@moudyellaz moudyellaz force-pushed the moudy/feat-group-key-holder branch from 9822584 to 1fdc2da Compare April 22, 2026 22:54
@moudyellaz moudyellaz marked this pull request as ready for review April 22, 2026 23:33
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.

1 participant