When a client wants to perform a read, it sends the request to
its assigned slave server. The slave executes the request, and
constructs a ``pledge'' packet which contains a copy of the
request, the secure hash (SHA-1 [1]) of the result,
and the latest time-stamped
value received
from the master. After signing this ``pledge'' packet, the slave
sends it to the client, together with the result of the query.
At the other end, the client first computes the secure hash of
the query result, and makes sure it matches the hash in the
``pledge'' packet. Then, the client verifies the slave's
signature on the ``pledge'' packet. Finally, the client makes
sure the time-stamp is not older than
.
If all
these conditions are met, the client accepts the answer,
otherwise it rejects it.
Because of the asynchronous nature of the network connection
between the client and the slave, it is possible that a result
that was ``fresh'' when sent by the slave, becomes stale by the
time it reaches the client. In such a situation, the client has
to drop the answer and try the query again. By carefully
selecting the value for
,
and the frequency
masters send ``keep-alive'' packets, the probability of such
events occuring can be reduced. However, clients with very slow
or unreliable network connections may never be able to get
fresh-enough responses. One possible way to accommodate such
clients is to relax the consistency model and allow the
to be set by the clients themselves. In this case,
clients with fast network connections can set high ``freshness''
requirements, while clients with slow connections can settle with
more modest expectations.
The main vulnerability of this basic read protocol is that a malicious slave can return wrong answers to client requests. To protect against such malicious slaves, we employ two techniques - probabilistic checking and auditing which will be described in the next two sections.