Skip to content

docs: Add replay protection example (#9)#163

Closed
yuzengbaao wants to merge 1 commit intoScottcjn:mainfrom
yuzengbaao:examples/replay-protection
Closed

docs: Add replay protection example (#9)#163
yuzengbaao wants to merge 1 commit intoScottcjn:mainfrom
yuzengbaao:examples/replay-protection

Conversation

@yuzengbaao
Copy link

This PR provides a reference implementation for replay-protected signature verification as requested in #9.

Included in examples/replay_protection.py:

  1. Timestamp TTL (Time-To-Live) window validation to reject stale payloads
  2. In-memory duplicate nonce caching
  3. Structured error responses for rejection cases
  4. Self-contained verifiable test vectors showing normal, duplicate, stale, out-of-bounds, and malformed inputs.

Closes #9.

@yuzengbaao yuzengbaao requested a review from Scottcjn as a code owner March 21, 2026 23:28
Copilot AI review requested due to automatic review settings March 21, 2026 23:28
@github-actions github-actions bot added the size/M PR: 51-200 lines label Mar 21, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new reference example demonstrating replay-protected signature verification for Beacon-style message handling (nonce uniqueness + timestamp TTL window), addressing the replay-protection gap described in issue #9.

Changes:

  • Introduces SecureMessageHandler that verifies Ed25519 signatures, enforces a TTL window, and rejects duplicate nonces.
  • Implements a simple in-memory NonceCache with TTL-based cleanup.
  • Provides a runnable set of test vectors (valid, duplicate nonce, stale timestamp, future timestamp, missing fields).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +2
"""Beacon Replay Protection Example.

Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

All other scripts in examples/ include a #!/usr/bin/env python3 shebang at the top; adding it here would keep the examples consistent and makes the file directly executable on Unix-like systems.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +86
ts = payload.get("ts")
nonce = payload.get("nonce")
sender = payload.get("agent_id")

if not ts or not nonce or not sender:
return {"error": "Missing replay protection fields (ts, nonce, agent_id)", "status": 400}

# 3. Validate Timestamp (TTL validation)
now = int(time.time())
age = now - ts

# Reject future timestamps (allowing 5s clock skew)
if age < -5:
return {"error": "Timestamp in the future", "status": 400}

if age > TIMESTAMP_TTL_SECONDS:
return {"error": f"Message expired. Age: {age}s, TTL: {TIMESTAMP_TTL_SECONDS}s", "status": 401}

# 4. Check Nonce (duplicate prevention)
if not self.nonce_cache.check_and_add(sender, nonce, now):
return {"error": "Replayed message (duplicate nonce)", "status": 401}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

This example trusts agent_id from the signed payload for nonce-scoping. An attacker can sign with a valid key but vary agent_id to bypass duplicate-nonce detection (and/or poison the cache). Consider deriving agent_id from pubkey_hex (via agent_id_from_pubkey) and rejecting the message if the payload’s agent_id doesn’t match the derived value; then use the derived ID for the cache key.

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +75

if not ts or not nonce or not sender:
return {"error": "Missing replay protection fields (ts, nonce, agent_id)", "status": 400}

# 3. Validate Timestamp (TTL validation)
now = int(time.time())
age = now - ts
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

payload.get("ts") is used as an int later (age = now - ts), but JSON may supply it as a string/float and this will raise a TypeError. Also, if not ts will treat ts=0 as “missing” even though the field exists. Consider validating/coercing ts to an int (and handling ValueError), and checking required fields with is None / explicit string checks rather than truthiness.

Suggested change
if not ts or not nonce or not sender:
return {"error": "Missing replay protection fields (ts, nonce, agent_id)", "status": 400}
# 3. Validate Timestamp (TTL validation)
now = int(time.time())
age = now - ts
# Explicitly check for missing fields; allow values like 0 for ts
if ts is None or nonce is None or sender is None:
return {"error": "Missing replay protection fields (ts, nonce, agent_id)", "status": 400}
# Coerce timestamp to int and validate type
try:
ts_int = int(ts)
except (TypeError, ValueError):
return {"error": "Invalid ts field; must be integer Unix timestamp", "status": 400}
# 3. Validate Timestamp (TTL validation)
now = int(time.time())
age = now - ts_int

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +68
try:
payload = json.loads(raw_payload.decode("utf-8"))
except json.JSONDecodeError:
return {"error": "Invalid JSON format", "status": 400}

# 2. Check for required replay protection fields
ts = payload.get("ts")
nonce = payload.get("nonce")
sender = payload.get("agent_id")
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

raw_payload.decode("utf-8") can raise UnicodeDecodeError, which currently isn’t caught, and json.loads(...) can return a non-dict JSON type (e.g., list) leading to an AttributeError on .get(...). Consider catching UnicodeDecodeError alongside JSONDecodeError and validating that the decoded JSON is an object/dict before accessing .get.

Copilot uses AI. Check for mistakes.
@Dlove123
Copy link
Contributor

🎯 Claiming this Bounty!

Plan:

  1. Add replay protection example
  2. Document best practices
  3. Write tests
  4. Submit PR

💰 Payment Information

RTC Address: RTCb72a1accd46b9ba9f22dbd4b5c6aad5a5831572b
GitHub: Dlove123

Quality Commitment:

  • ✅ Code Review before submission
  • ✅ Working example code

Let's build this! 🛡️

@Dlove123
Copy link
Contributor

💰 Payment Information (补充)

PayPal: 979749654@qq.com
ETH (Ethereum): 0x31e323edC293B940695ff04aD1AFdb56d473351D
RTC (RustChain): RTCb72a1accd46b9ba9f22dbd4b5c6aad5a5831572b
GitHub: Dlove123

⚠️ Payment Terms

  • Payment due within 30 days of PR merge
  • Reminder will be sent on Day 10/20/25 if unpaid
  • Code rollback on Day 30 if payment not received

@Dlove123
Copy link
Contributor

💰 Payment Information

PayPal: 979749654@qq.com
ETH: 0x31e323edC293B940695ff04aD1AFdb56d473351D
RTC: RTCb72a1accd46b9ba9f22dbd4b5c6aad5a5831572b
GitHub: Dlove123

@Scottcjn
Copy link
Owner

Closing — @yuzengbaao, we have seen a pattern of template/auto-generated PRs from this account across multiple Elyan Labs repos.

beacon-skill already has 15 working transports and a complete HeartbeatManager with Ed25519 signing, peer tracking, and on-chain anchoring. New transports need to follow existing patterns in beacon_skill/transports/ and integrate with the transport registry.

If you want to contribute genuinely: read the existing code, pick ONE transport, and submit a PR that works end-to-end with tests. Quality over quantity.

This is Sophia's House — we teach, we don't just reject. But we do require real code.

@Scottcjn Scottcjn closed this Mar 22, 2026
Dlove123 added a commit to Dlove123/beacon-skill that referenced this pull request Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M PR: 51-200 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Add replay-protected nonce/timestamp validation examples for Beacon message handlers

4 participants