Storj A Peer-to-Peer Cloud Storage Network
Storj A Peer-to-Peer Cloud Storage Network
Abstract
1
1 Introduction
Cloud storage has come to rely almost exclusively on large storage providers act-
ing as trusted third parties to transfer and store data. This system suffers from
the inherent weaknesses of a trust-based model. Because client-side encryp-
tion is nonstandard, the traditional cloud is vulnerable to a variety of security
threats, including man-in-the-middle attacks, malware, and application flaws
that expose private consumer and corporate data. Moreover, because many
storage devices rely on the same infrastructure, failures is correlated across files
and systems.
2 Design
Storj is a protocol that creates a distributed network for the formation and
execution of storage contracts between peers. The Storj protocol enables peers
on the network to negotiate contracts, transfer data, verify the integrity and
availability of remote data, retrieve data, and pay other nodes. Each peer is
an autonomous agent, capable of performing these actions without significant
human interaction. Many of the basic tools for these interactions are described
in this document. Full protocol documentation can be found elsewhere [1].
2
from the storage provider, or farmer, housing the data. The data owner retains
complete control over the encryption key, and thus over access to the data.
The data owner may separately secure knowledge of how a file is sharded
and where in the network the shards are located. As the set of shards in the
network grows, it becomes exponentially more difficult to locate any given shard
set without prior knowledge of their locations (see Section 6.3). This implies
that security of the file is proportional to the square of the size of the network.
Sharding large files like video content and distributing the shards across
nodes reduces the impact of content delivery on any given node. Bandwidth
demands are distributed more evenly across the network. In addition, the end-
user can take advantage of parallel transfer, similar to BitTorrent [2] or other
peer-to-peer networks.
2. Encrypted files are split into shards, or multiple files are combined to form
a shard.
3
3. Audit pre-processing is performed for each shard (see Section 2.3).
Similar to S/Kademlia [4], the Storj network requires peers to sign messages.
To join the network a node must create an ECDSA keypair, (kpriv , kpub ). The
Kademlia Node ID corresponds to ripemd160(sha256(kpub )). As such, each
Node ID in the Storj network is also a valid Bitcoin address, which the node
can spend from. Nodes sign all messages, and validate message signatures be-
fore processing messages. This modification enforces long-term identity on the
network, and provides a proof of work deterrent to Eclipse attacks on Kademlia
routing (see Section 5.2). In the future there are a variety of other uses for this
address.
Our reference implementation uses Merkle trees [5] and Merkle proofs. After
the sharding process the data owner generates a set of n random challenge salts
s0 , s1 , ...sn−1 and stores the set of salts s. The challenge salts are each prepended
to the data d, and the resulting string is hashed to form a pre-leaf p as such:
pi = H(si + d). Pre-leaves are hashed again, and the resulting digests become
4
the set of leaves l of a standard Merkle tree such that li = H(H(si + d)). The
leaf set is filled with hashes of a blank string until its cardinality is a power of
two, to simplify the proof process.
The data owner stores the set of challenges, the Merkle root and the depth
of the Merkle tree, then transmits the Merkle trees leaves to the farmer. The
farmer stores the leaves along with the shard. Periodically, the data owner
selects a challenge from the stored set, and transmits it to the farmer. Challenges
may be selected according to any reasonable pattern, but should not be reused.
The farmer uses the challenge and the data to generate the pre-leaf. The pre-
leaf, along with the set of leaves, is used to generate a Merkle proof, which is
sent back to the data owner.
The Storj Merkle proof always consists of exactly log2 (|l|) + 1 hashes, and
thus is a compact transmission, even for large trees. The data owner uses
the stored Merkle root and tree depth to verify the proof by verifying that its
length is equal to the tree depth and the hashes provided recreate the stored
root. This scheme does not allow false negatives or false positives, as the hash
function requires each bit to remain intact to produce the same output.
5
2.3.1 Partial Audits
The Merkle tree audit scheme requires significant computational overhead for
the data owner, as the entire shard must be hashed many times to generate
pre-leaves. An extension of this scheme utilizes subsets of the data to perform
partial audits, reducing computational overhead. This also has the advantage
of significantly reducing I/O burden on farmer resources.
Partial audits provide only probabilistic assurance that the farmer retains
the entire file. They allow for false positive results, where the verifier believes the
farmer retains the intact shard, when it has actually been modified or partially
deleted. The probability of a false positive on an individual partial audit is
easily calculable (see Section 6.4)
6
Thus the data owner can have a known confidence level that a shard is
still intact and available. In practice, this is more complex, as farmers may
implement intelligent strategies to attempt to defeat partial audits. Fortunately,
this is a bounded problem in the case of iterative audits. The probability of
several consecutive false positives becomes very low, even when small portions
of the file have been deleted.
In addition, partial audits can be easily mixed with full audits without re-
structuring the Merkle tree or modifying the proof verification process. Many
audit strategies that mix full and partial verification can be envisioned, each of
which provides different levels of confidence over time.
Other audit schemes were examined, but deemed generally unfeasible. For ex-
ample, Shacham and Waters proposed a compact proof [6] with several advan-
tages over Merkle-tree schemes. This construction allows for an endless stream
of challenges to be generated by the data owner with minimal stored informa-
tion. It also allows for public verifiability of challenge responses.
7
2.3.3 Issuing Audits
To issue audits, Storj extends the Kademlia message set with a new type: AU-
DIT (for a full list of Kademlia extensions, see Appendix A). These messages
are sent from data owners to farmers and contain the hash of the data and a
challenge. The farmer must respond with a Merkle proof as described above.
Upon receipt and validation of the Merkle proof, the data owner must issue
payment to the farmer according to agreed-upon terms.
Data storage is negotiated via a standard contract format [7]. The contract is a
versioned data structure that describes the relationship between data owner and
farmer. Contracts should contain all information necessary for each node to form
a relationship, transfer the data, create and respond to audits over time, and
arbitrate payments. This includes shard hash, shard size, audit strategy, and
payment information. Storj implements a publish/subscribe system to connect
parties interested in forming a contract (see Section 2.6).
Each party should store a signed copy of the contract. Contracts exist solely
for the benefit of the data owner and farmer, as no other node can verify the
terms or state of the relationship. In the future, contract information may be
stored in the DHT, or in an external ledger like a Blockchain, which may allow
some outside verification of relationship terms.
The contracting system extends Kademlia with four new message types:
OFFER, CONSIGN, MIRROR, and RETRIEVE.
8
scribe system described in Section 2.6. The OFFER message contains a fully-
constructed contract that describes the desired relationship. The two nodes
repeatedly swap signed OFFER messages. For each new message in the OF-
FER loop, the node either chooses to terminate negotiations, respond with a
new signed counter-offer, or accept the contract by countersigning it. Once an
OFFER is signed by both parties, they each store it locally, keyed by the hash
of the data.
2.5 Payment
Storj is payment agnostic. Neither the protocol nor the contract requires a
specific payment system. The current implementation assumes Storjcoin, but
many other payment types could be implemented, including BTC, Ether, ACH
transfer, or physical transfer of live goats.
9
inexpensive, audit payments are incredibly small, often below $0.000001 per
audit.
Storjcoin allows much more granular payments than other candidate curren-
cies, thereby minimizing trust between parties. In addition, the mechanics of
micropayment channels require the total value of the channel to be escrowed for
the life of the channel. This decreases currency velocity, and implies that value
fluctuations severely impact the economic incentives of micropayment channels.
The use of a separate token creates a certain amount of insulation from outside
volatility, and Storjcoin’s large supply minimizes the impact of token escrow on
the market.
New payment strategies must include a currency, a price for the storage, a
price for retrieval, and a payment destination. It is strongly advised that new
payment strategies consider how data owners prove payment, and farmers verify
receipt without human interaction. Micropayment networks, like the Lightning
Network [9], Implementation details of other payment strategies are left as an
exercise for interested parties.
2.6 Quasar
To operate Quasar, Storj extends Kademlia with three new message types:
SUBSCRIBE, UPDATE, and PUBLISH. These messages facilitate the creation
and propagation of filters. Each node maintains information about topics to
which it subscribes, as well as topics to which its neighbors subscribe in the
form of an attenuated Bloom filter with depth K = 3. The filter at level 1
represents the subscriptions of nodes one hop away.
UPDATE pushes local filter changes to the three nearest neighbors. When
10
a node subscribes to a new topic it must issue a SUBSCRIBE request to learn
about its neighbors states, then an UPDATE request to notify neighbors of its
new subscription. By exchanging filter lists via SUBSCRIBE/UPDATE loops
nodes gain progressively better knowledge of what information is desirable to
their neighbors, and to nodes reachable by their neighbors.
Cloud object stores typically own or lease servers to store their customers files.
They use RAID schemes or a multi-datacenter approach to protect the file from
physical or network failure. Because Storj objects exist in a distributed network
of untrusted peers, farmers should not be relied upon to employ the same safety
measures against data loss as a traditional cloud storage company. Indeed,
farmers may simply turn off their node at any time. As such, it is strongly
recommended that the data owner implement redundancy schemes to ensure the
safety of their file. Because the protocol deals only with contracts for individual
11
shards, many redundancy schemes may be used. Three are described below.
The simplest solution is to mirror shards across several nodes. Mirroring pro-
tects against hardware failures by ensuring that multiple copies of each shard
Qn
exist. Availability of the shard with this scheme is P = 1 − 0 an where an
is the uptime of the node storing shard n. Because all shards are required to
assemble the file, availability of the file is equal to the availability of the least
available shard. In the case of a dropped contract, a redundant copy of that
shard can be retrieved and a new location found for it on the network. This is
the current behavior of the reference implementation.
Storj will soon implement client-side Reed-Solomon erasure coding [14]. Erasure
coding algorithms break a file into k shards, and programmatically create m
parity shards, giving a total of k + m = n shards. Any k of these n shards can
be used to rebuild the file or any missing shards. Availability of the file is then
Qm
P = 1 − 0 am across the set of the m + 1 least available nodes. In the case
of loss of individual shards, the file can be retrieved, the missing shard rebuilt,
and then a new contract negotiated for the missing shard.
To prevent loss of the file, data owners should set shard loss tolerance levels.
Consider a 20-of-40 erasure coding scheme. A data owner might tolerate the loss
of 5 shards out of 40, knowing that the chance of 16 more becoming inaccessible
in the near future is low. However, at some point the probabilistic availability
will fall below safety thresholds. At that point the data owner must initiate a
retrieve and rebuild process.
Because node uptimes are known via the audit process, tolerance levels may
be optimized based on the characteristics of the nodes involved. Many strategies
may be implemented to handle this process.
12
2.8 KFS
To facilitate on-disk storage for farmers, Storj implements a local file store
called KFS [15]. The farming client initially used the filesystem directly to
store shards. Later the farming client used a single LevelDB instance. Both of
these approaches failed to scale. For example, LevelDB compaction processing
time scales linearly with store size, and locks both reads and writes while in
progress. This significantly impacted performance and availability for nodes
storing more than 100GB. KFS is an abstraction layer over a set of LevelDB
instances that seeks to address scaling problems.
2.8.1 Rationale
However, because LevelDB instances are cheap to create, open, and close,
compaction costs can be bounded by managing a set of size-limited LevelDB
instances. Instances can be initialized ad hoc, and opened and closed as neces-
sary.
With KFS compaction locks only individual buckets, leaving many others
available to read and write. Operations and compaction are distributed evenly
across hundreds of buckets, meaning the chance of an operation being blocked
by compaction is small. Whereas previously compaction would block the entire
shard set for several seconds (or longer on low-end hardware), it now blocks
only small sections of the shard set for a much shorter time period.
13
2.8.2 S-Buckets and Routing
Rather than a single large instance, KFS stores shards in B size-limited LevelDB
instances, called S-Buckets. Collectively S-Buckets L0 , L1 , LB−1 form the B-
Table. S-Buckets have a fixed maximum size in bytes, S. Thus the maximum
size of a KFS store is S ∗ B bytes. Storj currently uses S = 32 GiB and B = 256
for a total capacity of 8 TiB.
KFS requires that there be a reference identifier, which can be any arbitrary
R bit key where R ≥ log2 (B). Storj nodes will use their 160 bit Node ID.
Incoming shards are sorted into S-bucket according to the following method:
4. Let n = h ⊕ i.
S-Buckets that have reached S bytes cannot store more shards. Farmers
can determine if bucket Ln is full during contract negotiation by calculating n
14
using the data-hash field in the contract, and should reject contracts that would
cause them to overfill an S-Bucket. When all buckets in a KFS instance are
full, a second instance may be made. Given that the 8 TiB upper limit of a
farmers KFS instance is larger than most available drives, this is unlikely for
most hardware.
2. Prepend the chunk index string with ’0’ until it reaches length l.
In initial testing KFS outperforms vanilla LevelDB in reads, writes, and unlinks
for a variety of file sizes. KFS displays lower means and lower variance across
almost all combinations of file size, operation, and storage device type. It par-
ticularly excels at unlinks and writes of large files, reducing variance by several
15
orders of magnitude. The full methodology of these tests, and their results, can
be found elsewhere [16].
Due to the presence of NATs and other adverse network conditions, not all
devices are publicly accessible. To enable non-public nodes to participate in the
network, Storj implements a reverse tunnel system.
To facilitate this system, Storj extends Kademlia with three additional mes-
sage types: PROBE, FIND TUNNEL, and OPEN TUNNEL The tunneling sys-
tem also makes use of the publish/subscribe system detailed in section 2.6.
Nodes that receive a negative response to their initial PROBE should issue
a FIND TUNNEL request to any known node. That node must respond with
three contacts that have previously published a tunnel announcement via the
publish/subscribe system. Tunnel providers must be publicly addressable.
16
Once the non-public node has received a list of tunnel providers, it issues
OPEN TUNNEL requests to the tunnel providers. The providers must provide
a tunnel for that node if they are capable. To open a connection, the provider
sends back an affirmative response with tunnel information. The tunneled node
then opens a long-lived connection to the provider, and updates its own contact
information to reflect the tunnel address.
Data is transferred via HTTP [18]. Farmers expose endpoints where client
applications may upload or download shards. Clients’ requests are authenticated
via tokens provide by previous CONSIGN and RETRIEVE messages. This
transfer mechanism is not essential to the protocol, and many alternatives may
be implemented in the future.
3 Network Access
To enable simple access to the network from the widest possible array of
client applications, Storj implements a thin-client model that delegates trust to
a dedicated server that manages data ownership. This is similar to the SPV
wallet concept found in Bitcoin and other cryptocurrency ecosystems. The
burdens of the data owner can be split across the client and the server in a
variety of ways. By varying the amount of trust delegated, the server could also
provide a wide variety of other valuable services. This sort of dedicated server,
17
called Bridge, has been developed and released as Free Software. Any individual
or organization can run their own Bridge server to facilitate network access.
3.1 Bridge
Bridge is designed to store only metadata. It does not cache encrypted shards
and, with the exception of public buckets, does not hold encryption keys. The
only knowledge of the file that Bridge is able to share with third parties is
metadata such as access patterns. This system protects the client’s privacy and
gives the client complete control over access to the data, while delegating the
responsibility of keeping files available on the network to Bridge.
18
3.2 Bridge API and Client
Full documentation of the Bridge API is outside the scope of this whitepaper,
but is available elsewhere [19]. The first complete client implementation is in
JavaScript. Implementations in C, Python, and Java are in progress.
5. The client uses the IP addresses and tokens to contact the farming nodes
and upload the data.
6. The client transfers the audit information to the Bridge, delegating trust.
7. Bridge immediately issues an audit and verifies the response, to prove data
was transferred correctly.
8. Bridge assumes responsibility for issuing audits, paying farmers, and man-
aging file state.
2. Bridge validates the request and provides a list of farmer IP addresses and
tokens.
3. The client uses the addresses and tokens to retrieve the file
19
The JavaScript library accepts file, handles pre-processing, and manages
connections as directed by Bridge. It also makes decrypted downloads available
to applications as files, or as streams. A sample CLI using the library is available
as Free Software at https://github.com/storj/core-cli. It has been tested with a
wide variety of file sizes, and is capable of reliably streaming 1080p video from
the Storj network.
The primary function of Bridge and the Bridge API is to serve applications. To
this end clients and tools in a wide variety of languages are under development.
Key and file management tools for web backends are in early planning stages,
including Storj plugins for standard backend tools like content management
systems. These tools should help content-driven application developers work
with files on the Storj network. Standardizing these tools around permissioning
files by user could help create data portability between services as discussed in
section 4.2.
Bridges to other protocols and workflows are also planned. The Storj CLI
lends itself to shell scripting automation. Similar tools for FTP, FUSE, and
common tools for interacting with files will be developed in the future.
Bridge can be used to manage authorization for private files stored on the net-
work. Because Bridge manages the state of each contract under its care, it is
a logical provider of these services. It can manage a variety of authorization-
related services to enable sharing and collaboration.
20
3.3.1 Identity and Permissioning
The Bridge API uses public-key cryptography to verify clients. Rather than the
Bridge server issuing an API key to each user, users register public keys with
the Bridge. API requests are signed, and the Bridge verifies that the signature
matches a registered public key. Bridge organizes file metadata into buckets to
facilitate management. Buckets can be permissioned individually by registering
a set of public keys to the Bucket.
Because shard encryption keys are stored on the device that generated them,
data portability is an issue. The reference implementation of Bridge and the
client facilitate the transfer of file encryption keys between clients in a safe
way. Clients generate a cryptographically strong seed, by default a randomly
generated twelve word phrase. To encrypt a given file, the client generates a
key deterministically based on the seed, Bucket ID and File ID.
The user can import the seed one time to each new device, which per-
manantly keeps the devices syncronized. This also facilitates backup since users
only have to store the seed, not every newly generated file key.
Bridge, like other object stores, allows developers to create and disseminate
public files via public Buckets. The Bridge server allows the developer to upload
the encryption key, and then allows anonymous users to retrieve the file key
and the set of file pointers. Public Buckets are useful for content delivery to
webpages, or to public-facing applications.
A system to share and retrieve public files without need of a Bridge could
also be created. Pointers and keys could be posted publicly on any platform, and
clients could be required to pay farmers directly for downloads. In practice this
21
would be very similar to an incentivized torrent. Platforms serving pointers
function similarly to trackers facilitating torrents. It is unclear whether this
system would have significant advantages over existing torrent networks.
In the future, the Bridge could enable sharing of specific files between applica-
tions or users. Because all files co-exist on a shared network, this is a problem
of standardization and identity management.
Bridge could also use a third-party source of identity, like a PGP keyserver
or Keybase[21], to enable secure person-to-person file sharing. A tiered keying
strategy (as used by LastPass[20]) could also allow for the sharing of individual
files. Other cryptographic schemes like proxy re-encryption seem promising.
For a simplified example: if file keys are strongly encrypted and escrowed with
a Bridge, files could be shared to any social media handle that could be au-
thenticated via Keybase. Bridge could send the corresponding client a single
encrypted file key along with a transposition key, thus enabling access to a file
without exposing the file to Bridge, or modifying the file in any way.
As noted earlier, data owners are responsible for negotiating contracts and man-
aging file state. With enough information about peers on the network, contract
selection becomes a powerful tool for maintaining file state. A Bridge will have
many active contracts with many farmers, and will therefore have access to
information about those farmers. A Bridge could use this information to in-
telligently distribute shards across a set of farmers in order to achieve specific
performance goals.
For instance, via the execution of a contract, a Bridge node gathers data
about the farmers communication latency, audit success rate, audit response
latency, and availability. With minimal additional effort, the Bridge could also
gather information about the nodes available bandwidth. By gathering a large
pool of reliable data about farmers, a Bridge node can intelligently select a set of
farmers that collectively provides a probabilistic guarantee of a certain quality
22
of service.
In other words, the Bridge can leverage its knowledge about peers on the
network to tailor the service to the clients requirements. Rather than a limited
set of service tiers, a Bridge could assemble a package of contracts on the fly to
meet any service requirement. This allows the client to determine the optimal
latency, bandwidth, or location of a file, and have confidence that its goals will
be met. For instance, a streaming video application may specify a need for high
bandwidth, while archival storage needs only high availability. In a sufficiently
large network, any need could be met.
In cases where the cost of delegating trust is not excessively high, clients may
use third-party Bridges. Because Bridges do not store data and have no access
to keys, this is still a large improvement on the traditional data-center model.
Many of the features Bridge servers provide, like permissioning and intelligent
contracting, leverage considerable network effects. Data sets grow exponentially
more useful as they increse in size, indicating that there are strong economic
incentives to share infrastructure and information in a Bridge.
23
4 Future Areas of Research
Storj is a work in progress, and many features are planned for future versions.
There are relatively few examples of functional distributed systems at scale, and
many areas of research are still open.
Bridge nodes could cooperate to share data about the network in a mutually
beneficial federation. This would allow each Bridge to improve the quality of
service that it provides by improving the quality of information available.
Bridges could also, with the consent of users, cooperate to share file metadata
and pointers among themselves. This would allow a user to access their file from
any Bridge, rather than being dependent on a single Bridge. A tiered set of
fallback Bridges storing the same access information is a desirable feature, as it
hedges against downtime from a solo Bridge. Some solvable permissioning issues
may exist, but there is no reason to believe a standard format and algorithm
for syncing state across Bridges may not be developed.
By encouraging use of data format and access standards, Storj aims to allow
portability of data between applications. Unlike a traditional model, where
control of data is tied to the service used to access the data, data access may
be tied to individual users because Storj forms a common underlying layer.
User data can be tied to persistent cryptographic identities, and authenticated
without exposing data to third parties. Siloing data in applications is a harmful
relic of traditional models. Building cross-compatibility into the future of data
storage greatly improves user privacy and user experience.
24
to a web of trust identity via services like Keybase, or handled by a distributed
self-sovereign identity system. Smart contract systems, e.g. Ethereum [22] con-
tracts, seem like a sensible long-term choice, as they can provide file permissions
based on arbitrary code execution. Some problems may exist with respect to
management of the private information required for identity and permissioning
systems, but sufficient solutions likely exist.
While this system represents a significant step up in both usability and value,
there are unmitigable security issues. Unfortunately, as in any cryptographic
system, it is impossible to revoke access to data. Applications may cache data
or forward it to third parties. Users, by definition, trust application developers
to handle their data responsibly. To mitigate these risks, Storj Labs intends
to provide incentives to developers to build free and open-source software. No
application can be completely secure, but auditable code is the best defense of
users privacy and security.
The potential advantages in terms of user experience and privacy are great,
but more research is needed. Many open questions exist with respect to per-
missioning mechanisms. At worst a unified backend powering interoperable
applications provides equivalent security to current data-center based models.
Storj hopes to collaborate with other forward-thinking data-driven projects to
create and advocate for these open standards.
Storj, like many distributed networks, would profit immensely from a distributed
reputation system. A reliable means of determining reputation on a distributed
system is an unsolved problem. Several approaches have been detailed, and some
implemented in practice but none have achieved consensus among researchers
or engineers. A brief review of several of these approaches follows.
25
4.3.1 IPFS Bitswap
IPFS presents the concept of BitSwap ledgers [23]. BitSwap ledgers are simple
local accounting of past interactions with other nodes. As IPFS and the BitSwap
protocols are primarily concerned with distribution of objects in a Kademlia-
style DHT, BitSwap ledgers count bytes sent and bytes received. Rather than
attempt to reach global consensus about the reputation state of the system,
these ledgers deal only with one-to-one relationships, and do not account for
latency, bandwidth, availability, or other quality of service factors. Most of
the behavior of nodes is left to the implementer, with some discussion given to
potential exchange strategies. This implies that BitSwap ledgers scale well and
are extremely versatile.
4.3.3 TrustDavis
26
Denominating trust in terms of monetary value is attractive for an economic
network like Storj, but the mechanics of insurance contracts in a system like this
represent an extremely difficult problem. Notably, because failures to deliver
payment propagate backwards through the insurance route, the financial burden
always falls on the node that trusted an untrustworthy node, rather than the
untrustworthy nodes.
Many negotiation strategies can exist and interact via the OFFER loop. Full
exploration of negotiation strategies is beyond the scope of this paper, but a few
interesting areas are immediately apparent. Simple examples include price floors
and ceilings, but complex models could be built to base strategies on market
trends and the subjective value of a shard. Negotiation strategies executed by
autonomous agents are an area of (fascinating) ongoing research. Storj will be
one of the first large-scale machine-driven marketplaces. As such, improving
negotiation efficiency is critical to the long-term efficiency of the market.
27
5 Attacks
As with any distributed system, a variety of attack vectors exist. Many of these
are common to all distributed systems. Some are storage-specific, and will apply
to any distributed storage system.
5.1 Spartacus
5.2 Sybil
Sybil attacks involve the creation of large amounts of nodes in an attempt to dis-
rupt network operation by hijacking or dropping messages. Kademlia, because
it relies on message redundancy and a concrete distance metric, is reasonably
resistant to Sybil attacks. A nodes neighbors in the network are selected by
Node ID from an evenly distributed pool, and most messages are sent to at
least three neighbors. If a Sybil attacker controls 50% of the network, it suc-
cessfully isolates only 12.5% of honest nodes. While reliability and performance
will degrade, the network will still be functional until a large portion of the
network consists of colluding Sybil nodes.
5.2.1 Google
28
5.2.2 Honest Geppetto
5.3 Eclipse
An eclipse attack attempts to isolate a node or set of node in the network graph,
by ensuring that all outbound connections reach malicious nodes. Eclipse at-
tacks can be hard to identify, as malicious nodes can be made to function nor-
mally in most cases, only eclipsing certain important messages or information.
Storj addresses eclipse attacks by using public key hashes as Node IDs. In order
to eclipse any node in the network, the attacker must repeatedly generate key
pairs until it finds three keys whose hashes are closer to the targeted node than
its nearest non-malicious neighbor, and must defend that position against any
new nodes with closer IDs. This is, in essence, a proof-of-work problem whose
difficulty is proportional to the number of nodes in the network.
It follows that the best way to defend against eclipse attacks is to increase
the number of nodes in the network. For large networks it becomes prohibitively
expensive to perform an eclipse attack (see Section 6.2). Furthermore, any node
that suspects it has been eclipsed may trivially generate a new keypair and node
ID, thus restarting the proof-of-work challenge.
Because tunneled connections rely on the tunnel provider, it is trivial for a tun-
nel provider to eclipse nodes for which it provides tunneled connections. This
attack cannot affect publicly addressable nodes, so it can be trivially defeated
with proper configuration. This attack can be mitigated by encrypting messages
intended for tunneled nodes, thus removing the malicious tunnel provider’s abil-
29
ity to inspect and censor incoming messages. Like a typical eclipse attack, any
node that suspects it is the victim of a tunnel eclipse can easily generate a new
Node ID, and find a new tunnel.
A data owner may attempt to avoid paying a farmer for data storage by refus-
ing to verify a correct audit. In response the farmer may drop the data-owners
shard. This attack primarily poses a problem for any future distributed reputa-
tion system, as it is difficult for outside observers to verify the claims of either
party. There is no known practical publicly verifiable proof of storage, and no
known scheme for independently verifying that a privately verifiable audit was
issued or answered as claimed. This indicates that a cheating client attack is a
large unsolved problem for any reputation system.
While the farming software is built to require authentication via signature and
token before serving download requests, it is reasonable to imagine a modifica-
tion of the farming software that will provide shards to any paying requestor.
In a network dominated by faithless farmers, any third-party can aggregate and
inspect arbitrary shards present on the network.
However, even should faithless farmers dominate the network, data privacy is
not significantly compromised. Because the location of the shards that comprise
a given file is held solely by the data owner, it is prohibitively difficult to locate
a target file without compromising the owner (see Section 6.3). Storj is not
30
designed to protect against compromised data owners. In addition, should a
third-party gather all shards, strong client-side encryption protects the contents
of the file from inspection. The pointers and the encryption key may be secured
separately. In the current implementation of Bridge, the pointers and the keys
are held by the Bridge and the client, respectively.
A typical Merkle proof verification does not require the verifier to know the
depth of the tree. Instead the verifier is expected to have the data being vali-
dated. In the Storj audit tree, if the depth is unknown to the verifier the farmer
may attack the verification process by sending a Merkle proof for any hash in
the tree. This proof still generates the Merkle root, and is thus a valid proof of
some node. But, because the verifier does not hold the data used to generate
the tree, it has no way to verify that the proof is for the specific leaf that cor-
responds to the challenge. The verifier must store some information about the
bottom of the tree, such as the depth of the tree, the set of leaves nodes, or the
set of pre-leaves. Of these, the depth is most compact, and thus preferable.
6 Selected Calculations
The following are several interesting calculations related to the operation of the
network.
31
k−1
X
i n−i n
Prf ailure (n; k, p) = p (1 − p)
i=0
i
n k p Prf ailure n, k, p
18 6 0.5 4.812e-02
18 6 0.75 3.424e-05
18 6 0.9 5.266e-10
18 6 0.98 6.391e-19
36 12 0.5 1.440e-02
36 12 0.75 2.615e-08
36 12 0.9 1.977e-17
36 12 0.98 1.628e-34
Code:
1 def fac ( n ) : return 1 if n ==0 else n * fac (n -1)
2 def choose (n , k ) : return fac ( n ) / fac ( k ) / fac (n - k )
3 def bin (n ,k , p ) : return choose (n , k ) * p ** k * (1 - p ) ** (n - k )
4 def prob _ fail (n ,k , p ) : return sum ([ bin (n ,i , p ) for i in range (0 , k ) ])
Code:
32
1 def fac ( k ) : return 1 if k ==0 else k * fac (k -1)
2 def choose (h , k ) : return fac ( h ) / fac ( k ) / fac (h - k )
3 def bin (i ,h , k ) : return choose (h , i ) * k ** -i * (1 -(1.0/ k ) ) ** (h - i )
4 def prob _ succ (h , k ) : return sum ([ bin (i ,h , k ) for i in range (3 , h ) ])
Code:
1 def fac ( k ) : return 1 if k ==0 else k * fac (k -1)
2 def choose (h , k ) : return fac ( h ) / fac ( k ) / fac (h - k )
3 def hyp (N ,k , n ) : return choose (N -k ,n - k ) / float ( choose (N , n ) )
4 def prob _ success (N ,k , n ) : return hyp (N ,k , n )
33
6.4 Partial Audit Confidence Levels
Farmers attempting to game the system may rely on data owners to issue partial
audits. Partial audits allow false positives, where the data appears intact, but
in fact has been modified. Data owners may account for this by ascribing
confidence values to each partial audit, based on the likelihood of a false positive.
Partial audit results then update prior confidence of availability. Data owners
may adjust audit parameters to provide desired confidence levels.
Code:
1 def fac ( k ) : return 1 if k ==0 else k * fac (k -1)
2 def choose (h , k ) : return fac ( h ) / fac ( k ) / fac (h - k )
3 def hyp (N ,K , n ) : return float ( choose (N -K , n ) / choose (N , n )
4 def prob _ false _ pos (N ,K , n ) : return hyp (N ,K , n )
34
A List of Storj Message Types
A.1 Kademlia
A.2 Tunneling
A.3 Quasar
A.4 Contracting
13. MIRROR - Instruct a farmer to retrieve and mirror data from another
farmer.
35
References
[1] G. Hall. Storj core tutorial: Protocol specification, (2016).
https://storj.github.io/core/tutorial-protocol-spec.html.
[5] R.C. Merkle. Protocols for public key cryptosystems, (April 1980).
http://www.merkle.com/papers/Protocols.pdf.
36
[14] J. S. Plank. A tutorial on reed-solomon coding for fault-tolerance in
raid-like systems, (1996).
http://web.eecs.utk.edu/~plank/plank/papers/CS-96-332.pdf.
[23] J. Benet. Ipfs - content addressed, versioned, p2p file system, (2014).
https://github.com/ipfs/ipfs/blob/master/papers/ipfs-cap2pfs/
ipfs-p2p-file-system.pdf.
37