Skip to content

fix(relay): widen gift-wrap created_at drift window to NIP-59's two days - #4324

Open
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:fix/4192-gift-wrap-drift-window
Open

fix(relay): widen gift-wrap created_at drift window to NIP-59's two days#4324
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:fix/4192-gift-wrap-drift-window

Conversation

@iroiro147

Copy link
Copy Markdown

Summary

Fixes #4192.

NIP-59 instructs clients to randomize a gift wrap's created_at into the
past — "The created_at timestamp SHOULD be tweaked to thwart time-
analysis attacks" — canonically up to 2 days. nostr-tools and other
mainstream implementations do this by default. Buzz relay applies the
standard ±15-minute freshness window uniformly across kinds, which rejects
every spec-following NIP-17 client: wraps produced by nostr-tools
nip17.wrapEvent() (whose defaults backdate the wrap) are rejected 100%
of the time. No conforming NIP-17 client can deliver a private DM through
a Buzz relay.

The wrap's timestamp is deliberately meaningless; ordering and retention
decisions for gift wraps cannot rely on it anyway. The inner seal and
rumor timestamps are end-to-end encrypted and order on their own.

Changes

  • crates/buzz-relay/src/handlers/ingest.rs
    • New max_timestamp_drift_secs(kind) helper returning ±15 minutes for
      regular kinds and 2 * 24 * 3600 + 900 (2 days + NIP-59 skew margin)
      for KIND_GIFT_WRAP.
    • ingest_event_inner now uses the helper at the freshness check instead
      of a single constant.

Tests

  • ingest::tests::gift_wrap_drift_window_is_two_days_plus_skew — asserts
    max_timestamp_drift_secs(KIND_GIFT_WRAP) == 2*24*3600 + 900.
  • ingest::tests::non_gift_wrap_drift_window_is_tight_15_minutes — kinds
    1, 9, and KIND_LONG_FORM still get 900.

Verification

  • cargo check -p buzz-relay --lib — clean.
  • cargo test -p buzz-relay --lib --offline — 157/157 pass.
  • cargo fmt -p buzz-relay --check — clean.

Out of scope

  • Does not change the HTTP/WS transport gate (KIND_GIFT_WRAP already
    WebSocket-only at :1828) or the per-kind scope allowlist.
  • Does not weaken freshness for any non-gift-wrap kind.

Fixes block#4192.

NIP-59 instructs clients to randomize a gift wrap's `created_at` into the
past — "SHOULD be tweaked to thwart time-analysis attacks" — canonically
up to 2 days. `nostr-tools` and other mainstream implementations do this
by default. Buzz relay applies the standard ±15-minute freshness window
uniformly across kinds, which rejects every spec-following NIP-17 client:
the wrap produced by `nostr-tools` `nip17.wrapEvent()` (defaults to
backdating) is rejected 100% of the time.

The wrap's timestamp is deliberately meaningless; ordering and retention
decisions for gift wraps cannot rely on it anyway. The inner seal and
rumor timestamps are end-to-end encrypted and order on their own.

Change: introduce a `max_timestamp_drift_secs(kind)` helper that returns
±15 minutes for regular kinds and 2 days + 15 minutes skew margin for
KIND_GIFT_WRAP, then use it at the freshness check in `ingest_event_inner`.

Tests cover both branches of the helper — gift-wrap gets the wide window,
non-gift-wrap kinds stay at the tight 15-minute default.

`cargo check -p buzz-relay --lib` clean. `cargo test -p buzz-relay --lib
--offline` 157/157 pass including the 2 new tests. `cargo fmt --check`
clean.

Signed-off-by: Sarthak Singh <sarthak.singh@juspay.in>
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.

Relay rejects spec-compliant NIP-17 DMs: gift-wrap (kind 1059) timestamps fail the freshness window

1 participant