Skip to content

Commit 874d157

Browse files
committed
Revise the section on hashing and security
1 parent 65a2c1c commit 874d157

File tree

1 file changed

+58
-58
lines changed

1 file changed

+58
-58
lines changed

doc/final-report/integration-notes.md

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -141,66 +141,66 @@ https://github.com/IntersectMBO/ouroboros-network/pull/4951. We would advise to
141141
fix this Nix-related bug rather than downgrading `lsm-tree`’s dependency on
142142
`io-classes` to version 1.5.
143143

144-
# Security of hash based data structures
144+
# Security of hash-based data structures
145145

146146
Data structures based on hashing have to be considered carefully when they may
147-
be used with untrusted data. If the attacker can control the keys in a hash
148-
table for example, they may be able to arrange for all their keys to have hash
149-
collisions which may cause unexpected performance problems. This is why the
150-
Haskell Cardano node implementation does not use hash tables, and uses
151-
ordering-based containers instead (such as `Data.Map`).
152-
153-
The Bloom filters in an LSM tree are hash based data structures. For performance
154-
they do not use cryptographic hashes. So in principle it would be possibile for
155-
an attacker to arrange that all their keys hash to a common set of bits. This
156-
would be a potential problem for the UTxO and other stake related tables in
157-
Cardano, since it is the users that get to pick (with old modest grinding
158-
difficulty) their UTxO keys (TxIn) and stake keys (verification key hashes). It
159-
would be even more serious if an attacker can grind their set of malicious keys
160-
locally, in the knowledge that the same set of keys will hash the same way on
161-
all other Cardano nodes.
162-
163-
This issue was not considered in the original project specification, but we
164-
have considered it and included a mitigation. The mitigation is that on the
165-
initial creation of a lsm-tree session, a random salt is conjured (from
166-
`/dev/random`) and stored persistenly as part of the session. This salt is then
167-
used as part of the Bloom filter hashing for all runs in all tables in the
168-
session.
169-
170-
The result is that while it is in principle still possible to produce hash
171-
collisions in the Bloom filter, this now depends on knowing the salt. And now
172-
every node has a different salt. So a system wide attack becomes impossible;
173-
instead it is only plausible to target individual nodes. Discovering a node's
174-
salt would also be impractically difficult. In principle there is a timing
175-
side channel, in that collisions will cause more I/O and thus take longer.
176-
An attacker would need to get upstream of a victim node, supply a valid block
177-
and measure the timing of receiving the block downstream. There is however a
178-
large amount of noise.
179-
180-
Overall, our judgement is that this mitigation is practically sufficient, but
181-
it merits a securit review from others who may make a different judgement. It
182-
is also worth noting that this issue may occur in other LSM-trees used in other
183-
Cardano and non-Cardano implementations. In particular, RocksDB does not appear
184-
to use a salt at all.
185-
186-
Note that a per-run or per-table hash salt would incur non-trivial costs,
187-
because it would reduce the sharing available in bulk Bloom filter lookups
188-
(looking up N keys in M filters). The Bloom filter lookup is a performance
189-
sensitive part of the overall database implementation.
190-
191-
In the Cardano context, a downside of a per-session (and thus per-node) Bloom
192-
filter salt is that it may interact poorly with sharing of pre-created
193-
databases. While it will work to copy a whole database session (since this
194-
includes the salt), it means the salt is then shared between the nodes. If SPOs
195-
share databases widely with each other (to avoid syning the entire chain), then
196-
the salt diversity is lost. This would be especially acute with Mithril which
197-
shares a single copy of the database. It may be necesary for proper Mithril
198-
support to add a re-salting operation, and to perform this re-salting operation
199-
after cloning a Mithril snapshot. Re-salting would involve re-creating the
200-
Bloom filter for each table run, which involves reading each run and inserting
201-
into a new Bloom filter, and writing out the new Bloom filter. This would of
202-
course be additional development work, but the infrastructure needed is
203-
present already.
147+
be used with untrusted data. For example, an attacker who can control the keys
148+
in a hash table may be able to provoke hash collisions and cause unexpected
149+
performance problems this way. This is why the Haskell Cardano node
150+
implementation does not use hash tables but ordering-based containers, such as
151+
those provided by `Data.Map`.
152+
153+
The Bloom filters in an LSM-Tree are hash-based data structures. For the sake of
154+
performance, they do not use cryptographic hashes. Thus, without additional
155+
measures, an attacker can in principle choose keys whose hashs identify mostly
156+
the same bits. This is a potential problem for the UTxO and other stake-related
157+
tables in Cardano, since it is the users who get to pick their UTxO keys (TxIn)
158+
and stake keys (verification key hashes) and these keys will hash the same way
159+
on all other Cardano nodes.
160+
161+
This issue was not considered in the original project specification, but we have
162+
taken it into account and have included a mitigation in `lsm-tree`. The
163+
mitigation is that, on the initial creation of a session, a random salt is
164+
conjured and stored persistenly as part of the session. This salt is then used
165+
as part of the Bloom filter hashing for all runs in all tables of the session.
166+
167+
The consequence is that, while it is in principle still possible to produce hash
168+
collisions in the Bloom filter, this now depends on knowing the salt. However,
169+
every node has a different salt. Thus a system-wide attack becomes impossible.
170+
It is only plausible to target individual nodes, but discovering a node’s salt
171+
is extremely difficult. In principle there is a timing side channel, in that
172+
collisions will cause more I/O and thus cause longer running times. To exploit
173+
this, an attacker would need to get upstream of a victim node, supply a valid
174+
block and measure the timing of receiving the block downstream. There would,
175+
however, be a large amount of noise spoiling such an attack.
176+
177+
Overall, our judgement is that our mitigation is sufficient, but it merits a
178+
security review from others who may make a different judgement. It is also worth
179+
noting that the described hash clash issue may occur in other LSM-tree
180+
implementations used in other software, related and unrelated to Cardano. In
181+
particular, RocksDB does not appear to use a salt at all.
182+
183+
Note that using a per-run or per-table hash salt would incur non-trivial costs,
184+
because it would reduce the sharing available in bulk Bloom filter lookups,
185+
where several keys are looked up in several filters. Given that the Bloom filter
186+
lookup is a performance-sensitive part of the overall database implementation,
187+
such an approach to salting does not seem feasible.
188+
189+
In the Cardano context, a downside of picking Bloom filter salts per session and
190+
thus per node is that this interacts poorly with sharing of pre-created
191+
databases. While it would still be possible to copy a whole database session,
192+
since this includes the salt, doing so would result in the salt being shared
193+
between nodes. If SPOs shared databases widely with each other, to avoid
194+
processing the entire chain, then the salt diversity would be lost.
195+
196+
Picking Bloom filter salts per session is particularly problematic in Mithril,
197+
which shares a single copy of the database. It may be necessary for proper
198+
Mithril support to add a re-salting operation and to perform this operation
199+
after cloning a Mithril snapshot. Re-salting would involve re-creating the Bloom
200+
filters for all table runs, which would mean reading each run, inserting its
201+
keys into a new Bloom filter and finally writing out the new Bloom filter.
202+
Adding such a feature would, of course, incur additional development work, but
203+
the infrastructure needed is present already.
204204

205205
# Possible file system incompatibility with XFS
206206

0 commit comments

Comments
 (0)