Skip to content

feat: unify keystore file format with the relay RLN keystore (AppKeystore format) #10

Description

@chaitanyaprem

Context

logos-delivery currently ends up with two unrelated RLN keystore formats on disk:

  1. Mix keystore (this plugin, src/mix_rln_spam_protection/credentials.nim, default rln_keystore.json from constants.nim):

    {
      "keystore": [
        {
          "version": 1,
          "salt": "",
          "iv": "",
          "ciphertext": "",
          "membershipIndex": 5,
          "userMessageLimit": 100
        }
      ]
    }

    PBKDF2-HMAC-SHA256 (100k iters) → AES-256-CBC, no MAC; membershipIndex / userMessageLimit stored as plaintext metadata; effectively single-credential (only entries[0] is ever read).

  2. Relay RLN keystore (nwaku waku_keystore AppKeystore format, Ethereum-keyfile style):

    {
      "application": "waku-rln-relay",
      "appIdentifier": "01234567890abcdef",
      "version": "0.2",
      "credentials": {
        "<membership-hash>": { "crypto": { "cipher": "aes-128-ctr", "kdf": "pbkdf2", "mac": "", "...": "" } }
      }
    }

    Decrypted payload is a KeystoreMembership: membershipContract {chainId, address}, treeIndex, userMessageLimit, identityCredential — a superset of the mix entry. Has MAC integrity, 1M KDF iterations, encrypts everything, and supports multiple memberships per file.

The underlying credential is identical in both (BN254 RLN, idTrapdoor ‖ idNullifier ‖ idSecretHash ‖ idCommitment, 4×32 bytes), so unification is a serialization/plumbing exercise, not a crypto change.

Proposal

Adopt the AppKeystore container format in this plugin so a node can keep a single keystore file holding both relay and mix memberships.

Benefits beyond unification:

  • Adds MAC integrity verification (missing today)
  • Encrypts membershipIndex / userMessageLimit instead of leaking them as plaintext
  • Multi-membership support comes for free

Scope

File-level unification only. Relay and mix memberships remain separate credentials — we are NOT registering one identity in both groups (a shared idCommitment would publicly link the node's relay and mix identities; that is a separate, deliberate decision if ever).

Open points

  1. Discriminating mix vs relay entries. Mix memberships are off-chain (registered via relay broadcast), so membershipContract has no natural value. Options: sentinel values (chainId: "", address: "mix"), or a distinct application value (e.g. "mix-rln") as a separate app-keystore document in the same file. Note: nwaku's loadAppKeystore currently errors on application mismatch instead of skipping to the next document — the multi-app-per-file route needs that changed to skip.
  2. Where the code lives. The plugin is standalone and shouldn't depend on logos-delivery. Either extract waku_keystore into a shared nimble package, or reimplement the (small, well-specified) keyfile read/write here.
  3. Config plumbing in logos-delivery. waku_mix/protocol.nim currently hardcodes rln_keystore_<peerId>.json and password "mix-rln-password" — should become config-driven (mirroring --rln-relay-cred-path / --rln-relay-cred-password), keeping the per-peerId scheme only for simulations/mixnet/.
  4. Migration. Detect old format ("keystore" key vs "application" key), decrypt, re-encrypt as a KeystoreMembership entry.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions