Skip to content

docs: RegisterDelegate is undocumented, and the predecessors variant says secrets are "sealed client-side" #97

Description

@sanity

RegisterDelegate has no docs, and RegisterDelegateWithPredecessors says secrets are "sealed client-side"

src/client_api/client_events.rs (0.8.5):

  • DelegateRequest::RegisterDelegate (line 591) has no rustdoc at all. It carries cipher: [u8; 32] and nonce: [u8; 24], and it is the first thing an integrator reads when deciding what to put in those fields.

  • RegisterDelegateWithPredecessors (lines 615-619) documents the ignore, but explains it wrongly:

    cipher and nonce mirror [DelegateRequest::RegisterDelegate] purely for field-shape parity. The node has ignored both since freenet-core#4140 (secrets are sealed client-side), so they carry no behavior here either.

    The parenthetical is not true. Delegate secrets are sealed by the node, under a per-delegate DEK the node derives with HKDF-SHA256(salt = delegate key, ikm = node KEK) — see SecretsStore::derive_delegate_dek in freenet-core. The node holds the plaintext during store_secret and re-derives the key on every read. The reason the client's cipher is ignored is the opposite of client-side sealing: accepting a client-supplied key would let a malicious or buggy client substitute a key the node operator does not control.

Why it matters

0.8 removed DelegateRequest::DEFAULT_CIPHER / DEFAULT_NONCE, so every app bumping 0.6 → 0.8 has to decide what to send in their place. The honest answer is "any value, the node discards it". A reader who lands on the current text concludes either that the bytes are load-bearing key material worth persisting (from the undocumented variant), or that the node never sees their plaintext (from the parenthetical). Both lead somewhere wrong, and the second is the more dangerous, since it overstates a security property.

This is the stdlib half of freenet/freenet-core#5265, where the equivalent stale claims in core have been corrected (freenet/freenet-core#5472). Core can't close this one: an integrator reads the stdlib types first.

Suggested fix

Give RegisterDelegate a rustdoc saying cipher/nonce are retained for wire-format compatibility and discarded by the node, which derives the delegate's local-scope DEK from its own KEK — so any value works, including all-zero bytes (the fields are fixed-size, so "any value" means any bytes, not an empty slice). Then drop the "sealed client-side" parenthetical from RegisterDelegateWithPredecessors and point both at the same explanation. Citing freenet-core#4146 alongside #4140 makes the history findable.

[AI-assisted - Claude]

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions