]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
download: make plaintext and ciphertext hashes in the UEB optional.
authorBrian Warner <warner@lothar.com>
Sun, 23 Mar 2008 21:46:49 +0000 (14:46 -0700)
committerBrian Warner <warner@lothar.com>
Sun, 23 Mar 2008 21:46:49 +0000 (14:46 -0700)
Removing the plaintext hashes can help with the guess-partial-information
attack. This does not affect compatibility, but if and when we actually
remove any hashes from the share, that will introduce a
forwards-compatibility break: tahoe-0.9 will not be able to read such files.

src/allmydata/download.py
src/allmydata/test/test_encode.py

index 66c399c5e1df7baa31c98ded99fb67e0e843fb56..2485ec10e882f9142ac22bf83e7a94edb359f9b2 100644 (file)
@@ -664,11 +664,13 @@ class FileDownloader:
         self._tail_codec = codec.get_decoder_by_name(d['codec_name'])
         self._tail_codec.set_serialized_params(d['tail_codec_params'])
 
-        crypttext_hash = d['crypttext_hash']
-        assert isinstance(crypttext_hash, str)
-        assert len(crypttext_hash) == 32
+        crypttext_hash = d.get('crypttext_hash', None) # optional
+        if crypttext_hash:
+            assert isinstance(crypttext_hash, str)
+            assert len(crypttext_hash) == 32
         self._crypttext_hash = crypttext_hash
-        self._plaintext_hash = d['plaintext_hash']
+        self._plaintext_hash = d.get('plaintext_hash', None) # optional
+
         self._roothash = d['share_root_hash']
 
         self._segment_size = segment_size = d['segment_size']
@@ -682,12 +684,18 @@ class FileDownloader:
         self._get_hashtrees_started = time.time()
         if self._status:
             self._status.set_status("Retrieving Hash Trees")
-        d = self._get_plaintext_hashtrees()
+        d = defer.maybeDeferred(self._get_plaintext_hashtrees)
         d.addCallback(self._get_crypttext_hashtrees)
         d.addCallback(self._setup_hashtrees)
         return d
 
     def _get_plaintext_hashtrees(self):
+        # plaintext hashes are optional. If the root isn't in the UEB, then
+        # the share will be holding an empty list. We don't even bother
+        # fetching it.
+        if "plaintext_root_hash" not in self._uri_extension_data:
+            self._plaintext_hashtree = None
+            return
         def _validate_plaintext_hashtree(proposal, bucket):
             if proposal[0] != self._uri_extension_data['plaintext_root_hash']:
                 self._fetch_failures["plaintext_hashroot"] += 1
@@ -713,6 +721,10 @@ class FileDownloader:
         return d
 
     def _get_crypttext_hashtrees(self, res):
+        # crypttext hashes are optional too
+        if "crypttext_root_hash" not in self._uri_extension_data:
+            self._crypttext_hashtree = None
+            return
         def _validate_crypttext_hashtree(proposal, bucket):
             if proposal[0] != self._uri_extension_data['crypttext_root_hash']:
                 self._fetch_failures["crypttext_hashroot"] += 1
@@ -915,12 +927,12 @@ class FileDownloader:
             self._results.timings["total"] = now - self._started
             self._results.timings["segments"] = now - self._started_fetching
         self._output.close()
-        if self.check_crypttext_hash:
+        if self.check_crypttext_hash and self._crypttext_hash:
             _assert(self._crypttext_hash == self._output.crypttext_hash,
                     "bad crypttext_hash: computed=%s, expected=%s" %
                     (base32.b2a(self._output.crypttext_hash),
                      base32.b2a(self._crypttext_hash)))
-        if self.check_plaintext_hash:
+        if self.check_plaintext_hash and self._plaintext_hash:
             _assert(self._plaintext_hash == self._output.plaintext_hash,
                     "bad plaintext_hash: computed=%s, expected=%s" %
                     (base32.b2a(self._output.plaintext_hash),
index cfd3bbf7ee6626b7d3ea406d1164b8e70f62ada7..b8ad896154376fb991a3b64368600109f7285498 100644 (file)
@@ -25,8 +25,8 @@ class FakeBucketWriterProxy:
     def __init__(self, mode="good"):
         self.mode = mode
         self.blocks = {}
-        self.plaintext_hashes = None
-        self.crypttext_hashes = None
+        self.plaintext_hashes = []
+        self.crypttext_hashes = []
         self.block_hashes = None
         self.share_hashes = None
         self.closed = False
@@ -57,14 +57,14 @@ class FakeBucketWriterProxy:
     def put_plaintext_hashes(self, hashes):
         def _try():
             assert not self.closed
-            assert self.plaintext_hashes is None
+            assert not self.plaintext_hashes
             self.plaintext_hashes = hashes
         return defer.maybeDeferred(_try)
 
     def put_crypttext_hashes(self, hashes):
         def _try():
             assert not self.closed
-            assert self.crypttext_hashes is None
+            assert not self.crypttext_hashes
             self.crypttext_hashes = hashes
         return defer.maybeDeferred(_try)