Skip to content

feat: byzantine fault tolerant problem #15

@Ankesh2004

Description

@Ankesh2004

We were assuming that all nodes are trusted for now...
But we anyone knowing the chunkKey can come and request for deletion via tombstone or pollute the routing table.

Data plane is robust but the control plane is not that secure.

IN-DEPTH ANALYSIS: what if a node in the network is hijacked?

What a Hijacked Node CANNOT Do

These are already defended by the existing code:

1. Serve you corrupted chunks ✅ Blocked

This is the most important one. verifyChunkHash() in chunked_ops.go:

func verifyChunkHash(expectedKey string, data []byte) error {
    actual := sha256.Sum256(data)
    actualHex := hex.EncodeToString(actual[:])
    if actualHex != expectedKey {
        return fmt.Errorf("chunk integrity failed: expected %s, got %s", ...)
    }
    return nil
}

A hijacked node sends you garbage bytes claiming they're chunk "a3f9...". Your node computes SHA256(garbage) = "zz11..." — doesn't match — rejected before hitting disk. This is the core power of content-addressing. The hash IS the trust.

2. Overwrite an existing chunk ✅ Impossible

The chunk key is the SHA-256 of the content. To overwrite chunk "a3f9..." with malicious data, the attacker would need to find data whose SHA-256 is also "a3f9..." — that's a SHA-256 preimage attack, computationally infeasible.

3. Read your files ✅ Blocked by encryption

Even if the hijacked node has all your chunks, they're ChaCha20-Poly1305 ciphertext. Useless without myKey.key, which never leaves your machine.

4. Fake/modify a manifest ✅ Blocked

The manifest is stored under CID + ".manifest", where CID = SHA256(entire encrypted content). A fake manifest would produce a different CID — it can't silently replace yours.


What a Hijacked Node CAN Do

These are real threats with no current defense:

1. ❌ Issue fake tombstones (when deletion is added)

This is exactly the auth problem from before. Without signatures, a hijacked node can broadcast MessageDeleteFile{CID: yourCID} and every peer will comply.

Fix: Ed25519 signatures on tombstones — only the key that produced chunkKey can sign the deletion.

2. ❌ Eclipse attack — poison your routing table

The hijacked node sends MessageFindNodeResponse or MessagePeerExchange filled with fake/attacker-controlled addresses. Your DHT routing table fills with malicious nodes. Now all your chunk requests go to the attacker.

// attacker's PeerExchange — shares only their own fake nodes
KnownPeers: []PeerInfo{
    {ID: fakeID1, Addr: "attacker1:7000"},
    {ID: fakeID2, Addr: "attacker2:7000"},
    // ... 20 more attacker nodes
}

Now DHT.NearestNodes() returns only attacker nodes for every lookup.

Fix: Enforce routing table diversity — cap how many entries from a single /24 subnet, validate that peers are actually reachable before trusting them.

3. ❌ Sybil attack — fake many node identities

Node IDs in this system are just SHA256(arbitraryString). Generate a million IDs, flood the routing table.

// nothing stops this:
nodeID, _ = server.LoadOrGenerateNodeID(dataDir)  // just a random hex string

Fix: Proof-of-work on node IDs (generate IDs whose hash has N leading zeros) or a trusted identity registry.

4. ❌ Selective refusal (selfish node)

Just never respond to MessageGetChunk for your files. No timeout, no retry-from-different-node logic. Your fetchSingleChunk waits 5 seconds and gives up:

case <-timer.C:
    fmt.Printf("WARNING: chunk %s not received after 5s timeout\n", ...)

No fallback to a different peer — the fetch just fails.

Fix: On timeout, explicitly try the next DHT-nearest node instead of giving up.

5. ❌ Store garbage to fill DHT space near your file

An attacker generates keys close to your CID in the DHT keyspace (feasible to brute-force since IDs are just SHA-256 of arbitrary strings). They become "K-nearest" to your file and when you replicate — your chunks go to them. They store or discard as they please.


The Core Security Model

Trust model today:
┌─────────────────────────────────────────────────┐
│  Transport layer      → encrypted (X25519 AEAD) │
│  Chunk content        → hash-verified            │
│  File plaintext       → encrypted (user key)    │
│                                                 │
│  Routing / Discovery  → ⚠️  TRUSTED              │
│  Tombstone/deletion   → ⚠️  TRUSTED              │
│  Node identity        → ⚠️  TRUSTED              │
└─────────────────────────────────────────────────┘

The data plane (chunks, encryption) is actually quite robust. The control plane (who routes where, who gets to delete, who counts as a valid node) has zero adversarial hardening.

This is normal for this stage of a DFS project — BitTorrent had the same shape for years before DHT was hardened against Sybil attacks. The honest answer is:

This system is designed for trusted-node networks (your own machines, friends, a private cluster). For an open public internet deployment where any stranger can join, the control plane needs significant hardening before you'd want to rely on it.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions