| 1 | """ |
|---|
| 2 | Ported to Python 3. |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | from ..common import AsyncTestCase |
|---|
| 6 | from foolscap.api import flushEventualQueue |
|---|
| 7 | from allmydata.monitor import Monitor |
|---|
| 8 | from allmydata.mutable.common import CorruptShareError |
|---|
| 9 | from .util import PublishMixin, corrupt, CheckerMixin |
|---|
| 10 | |
|---|
| 11 | class Checker(AsyncTestCase, CheckerMixin, PublishMixin): |
|---|
| 12 | def setUp(self): |
|---|
| 13 | super(Checker, self).setUp() |
|---|
| 14 | return self.publish_one() |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | def test_check_good(self): |
|---|
| 18 | d = self._fn.check(Monitor()) |
|---|
| 19 | d.addCallback(self.check_good, "test_check_good") |
|---|
| 20 | return d |
|---|
| 21 | |
|---|
| 22 | def test_check_mdmf_good(self): |
|---|
| 23 | d = self.publish_mdmf() |
|---|
| 24 | d.addCallback(lambda ignored: |
|---|
| 25 | self._fn.check(Monitor())) |
|---|
| 26 | d.addCallback(self.check_good, "test_check_mdmf_good") |
|---|
| 27 | return d |
|---|
| 28 | |
|---|
| 29 | def test_check_no_shares(self): |
|---|
| 30 | for shares in list(self._storage._peers.values()): |
|---|
| 31 | shares.clear() |
|---|
| 32 | d = self._fn.check(Monitor()) |
|---|
| 33 | d.addCallback(self.check_bad, "test_check_no_shares") |
|---|
| 34 | return d |
|---|
| 35 | |
|---|
| 36 | def test_check_mdmf_no_shares(self): |
|---|
| 37 | d = self.publish_mdmf() |
|---|
| 38 | def _then(ignored): |
|---|
| 39 | for share in list(self._storage._peers.values()): |
|---|
| 40 | share.clear() |
|---|
| 41 | d.addCallback(_then) |
|---|
| 42 | d.addCallback(lambda ignored: |
|---|
| 43 | self._fn.check(Monitor())) |
|---|
| 44 | d.addCallback(self.check_bad, "test_check_mdmf_no_shares") |
|---|
| 45 | return d |
|---|
| 46 | |
|---|
| 47 | def test_check_not_enough_shares(self): |
|---|
| 48 | for shares in list(self._storage._peers.values()): |
|---|
| 49 | for shnum in list(shares.keys()): |
|---|
| 50 | if shnum > 0: |
|---|
| 51 | del shares[shnum] |
|---|
| 52 | d = self._fn.check(Monitor()) |
|---|
| 53 | d.addCallback(self.check_bad, "test_check_not_enough_shares") |
|---|
| 54 | return d |
|---|
| 55 | |
|---|
| 56 | def test_check_mdmf_not_enough_shares(self): |
|---|
| 57 | d = self.publish_mdmf() |
|---|
| 58 | def _then(ignored): |
|---|
| 59 | for shares in list(self._storage._peers.values()): |
|---|
| 60 | for shnum in list(shares.keys()): |
|---|
| 61 | if shnum > 0: |
|---|
| 62 | del shares[shnum] |
|---|
| 63 | d.addCallback(_then) |
|---|
| 64 | d.addCallback(lambda ignored: |
|---|
| 65 | self._fn.check(Monitor())) |
|---|
| 66 | d.addCallback(self.check_bad, "test_check_mdmf_not_enougH_shares") |
|---|
| 67 | return d |
|---|
| 68 | |
|---|
| 69 | |
|---|
| 70 | def test_check_all_bad_sig(self): |
|---|
| 71 | d = corrupt(None, self._storage, 1) # bad sig |
|---|
| 72 | d.addCallback(lambda ignored: |
|---|
| 73 | self._fn.check(Monitor())) |
|---|
| 74 | d.addCallback(self.check_bad, "test_check_all_bad_sig") |
|---|
| 75 | return d |
|---|
| 76 | |
|---|
| 77 | def test_check_mdmf_all_bad_sig(self): |
|---|
| 78 | d = self.publish_mdmf() |
|---|
| 79 | d.addCallback(lambda ignored: |
|---|
| 80 | corrupt(None, self._storage, 1)) |
|---|
| 81 | d.addCallback(lambda ignored: |
|---|
| 82 | self._fn.check(Monitor())) |
|---|
| 83 | d.addCallback(self.check_bad, "test_check_mdmf_all_bad_sig") |
|---|
| 84 | return d |
|---|
| 85 | |
|---|
| 86 | def test_verify_mdmf_all_bad_sharedata(self): |
|---|
| 87 | d = self.publish_mdmf() |
|---|
| 88 | # On 8 of the shares, corrupt the beginning of the share data. |
|---|
| 89 | # The signature check during the servermap update won't catch this. |
|---|
| 90 | d.addCallback(lambda ignored: |
|---|
| 91 | corrupt(None, self._storage, "share_data", list(range(8)))) |
|---|
| 92 | # On 2 of the shares, corrupt the end of the share data. |
|---|
| 93 | # The signature check during the servermap update won't catch |
|---|
| 94 | # this either, and the retrieval process will have to process |
|---|
| 95 | # all of the segments before it notices. |
|---|
| 96 | d.addCallback(lambda ignored: |
|---|
| 97 | # the block hash tree comes right after the share data, so if we |
|---|
| 98 | # corrupt a little before the block hash tree, we'll corrupt in the |
|---|
| 99 | # last block of each share. |
|---|
| 100 | corrupt(None, self._storage, "block_hash_tree", [8, 9], -5)) |
|---|
| 101 | d.addCallback(lambda ignored: |
|---|
| 102 | self._fn.check(Monitor(), verify=True)) |
|---|
| 103 | # The verifier should flag the file as unhealthy, and should |
|---|
| 104 | # list all 10 shares as bad. |
|---|
| 105 | d.addCallback(self.check_bad, "test_verify_mdmf_all_bad_sharedata") |
|---|
| 106 | def _check_num_bad(r): |
|---|
| 107 | self.failIf(r.is_recoverable()) |
|---|
| 108 | smap = r.get_servermap() |
|---|
| 109 | self.failUnlessEqual(len(smap.get_bad_shares()), 10) |
|---|
| 110 | d.addCallback(_check_num_bad) |
|---|
| 111 | return d |
|---|
| 112 | |
|---|
| 113 | def test_check_all_bad_blocks(self): |
|---|
| 114 | d = corrupt(None, self._storage, "share_data", [9]) # bad blocks |
|---|
| 115 | # the Checker won't notice this.. it doesn't look at actual data |
|---|
| 116 | d.addCallback(lambda ignored: |
|---|
| 117 | self._fn.check(Monitor())) |
|---|
| 118 | d.addCallback(self.check_good, "test_check_all_bad_blocks") |
|---|
| 119 | return d |
|---|
| 120 | |
|---|
| 121 | |
|---|
| 122 | def test_check_mdmf_all_bad_blocks(self): |
|---|
| 123 | d = self.publish_mdmf() |
|---|
| 124 | d.addCallback(lambda ignored: |
|---|
| 125 | corrupt(None, self._storage, "share_data")) |
|---|
| 126 | d.addCallback(lambda ignored: |
|---|
| 127 | self._fn.check(Monitor())) |
|---|
| 128 | d.addCallback(self.check_good, "test_check_mdmf_all_bad_blocks") |
|---|
| 129 | return d |
|---|
| 130 | |
|---|
| 131 | def test_verify_good(self): |
|---|
| 132 | d = self._fn.check(Monitor(), verify=True) |
|---|
| 133 | d.addCallback(self.check_good, "test_verify_good") |
|---|
| 134 | return d |
|---|
| 135 | |
|---|
| 136 | def test_verify_all_bad_sig(self): |
|---|
| 137 | d = corrupt(None, self._storage, 1) # bad sig |
|---|
| 138 | d.addCallback(lambda ignored: |
|---|
| 139 | self._fn.check(Monitor(), verify=True)) |
|---|
| 140 | d.addCallback(self.check_bad, "test_verify_all_bad_sig") |
|---|
| 141 | return d |
|---|
| 142 | |
|---|
| 143 | def test_verify_one_bad_sig(self): |
|---|
| 144 | d = corrupt(None, self._storage, 1, [9]) # bad sig |
|---|
| 145 | d.addCallback(lambda ignored: |
|---|
| 146 | self._fn.check(Monitor(), verify=True)) |
|---|
| 147 | d.addCallback(self.check_bad, "test_verify_one_bad_sig") |
|---|
| 148 | return d |
|---|
| 149 | |
|---|
| 150 | def test_verify_one_bad_block(self): |
|---|
| 151 | d = corrupt(None, self._storage, "share_data", [9]) # bad blocks |
|---|
| 152 | # the Verifier *will* notice this, since it examines every byte |
|---|
| 153 | d.addCallback(lambda ignored: |
|---|
| 154 | self._fn.check(Monitor(), verify=True)) |
|---|
| 155 | d.addCallback(self.check_bad, "test_verify_one_bad_block") |
|---|
| 156 | d.addCallback(self.check_expected_failure, |
|---|
| 157 | CorruptShareError, "block hash tree failure", |
|---|
| 158 | "test_verify_one_bad_block") |
|---|
| 159 | return d |
|---|
| 160 | |
|---|
| 161 | def test_verify_one_bad_sharehash(self): |
|---|
| 162 | d = corrupt(None, self._storage, "share_hash_chain", [9], 5) |
|---|
| 163 | d.addCallback(lambda ignored: |
|---|
| 164 | self._fn.check(Monitor(), verify=True)) |
|---|
| 165 | d.addCallback(self.check_bad, "test_verify_one_bad_sharehash") |
|---|
| 166 | d.addCallback(self.check_expected_failure, |
|---|
| 167 | CorruptShareError, "corrupt hashes", |
|---|
| 168 | "test_verify_one_bad_sharehash") |
|---|
| 169 | return d |
|---|
| 170 | |
|---|
| 171 | def test_verify_one_bad_encprivkey(self): |
|---|
| 172 | d = corrupt(None, self._storage, "enc_privkey", [9]) # bad privkey |
|---|
| 173 | d.addCallback(lambda ignored: |
|---|
| 174 | self._fn.check(Monitor(), verify=True)) |
|---|
| 175 | d.addCallback(self.check_bad, "test_verify_one_bad_encprivkey") |
|---|
| 176 | d.addCallback(self.check_expected_failure, |
|---|
| 177 | CorruptShareError, "invalid privkey", |
|---|
| 178 | "test_verify_one_bad_encprivkey") |
|---|
| 179 | return d |
|---|
| 180 | |
|---|
| 181 | def test_verify_one_bad_encprivkey_uncheckable(self): |
|---|
| 182 | d = corrupt(None, self._storage, "enc_privkey", [9]) # bad privkey |
|---|
| 183 | readonly_fn = self._fn.get_readonly() |
|---|
| 184 | # a read-only node has no way to validate the privkey |
|---|
| 185 | d.addCallback(lambda ignored: |
|---|
| 186 | readonly_fn.check(Monitor(), verify=True)) |
|---|
| 187 | d.addCallback(self.check_good, |
|---|
| 188 | "test_verify_one_bad_encprivkey_uncheckable") |
|---|
| 189 | return d |
|---|
| 190 | |
|---|
| 191 | |
|---|
| 192 | def test_verify_mdmf_good(self): |
|---|
| 193 | d = self.publish_mdmf() |
|---|
| 194 | d.addCallback(lambda ignored: |
|---|
| 195 | self._fn.check(Monitor(), verify=True)) |
|---|
| 196 | d.addCallback(self.check_good, "test_verify_mdmf_good") |
|---|
| 197 | return d |
|---|
| 198 | |
|---|
| 199 | |
|---|
| 200 | def test_verify_mdmf_one_bad_block(self): |
|---|
| 201 | d = self.publish_mdmf() |
|---|
| 202 | d.addCallback(lambda ignored: |
|---|
| 203 | corrupt(None, self._storage, "share_data", [1])) |
|---|
| 204 | d.addCallback(lambda ignored: |
|---|
| 205 | self._fn.check(Monitor(), verify=True)) |
|---|
| 206 | # We should find one bad block here |
|---|
| 207 | d.addCallback(self.check_bad, "test_verify_mdmf_one_bad_block") |
|---|
| 208 | d.addCallback(self.check_expected_failure, |
|---|
| 209 | CorruptShareError, "block hash tree failure", |
|---|
| 210 | "test_verify_mdmf_one_bad_block") |
|---|
| 211 | return d |
|---|
| 212 | |
|---|
| 213 | |
|---|
| 214 | def test_verify_mdmf_bad_encprivkey(self): |
|---|
| 215 | d = self.publish_mdmf() |
|---|
| 216 | d.addCallback(lambda ignored: |
|---|
| 217 | corrupt(None, self._storage, "enc_privkey", [0])) |
|---|
| 218 | d.addCallback(lambda ignored: |
|---|
| 219 | self._fn.check(Monitor(), verify=True)) |
|---|
| 220 | d.addCallback(self.check_bad, "test_verify_mdmf_bad_encprivkey") |
|---|
| 221 | d.addCallback(self.check_expected_failure, |
|---|
| 222 | CorruptShareError, "privkey", |
|---|
| 223 | "test_verify_mdmf_bad_encprivkey") |
|---|
| 224 | return d |
|---|
| 225 | |
|---|
| 226 | |
|---|
| 227 | def test_verify_mdmf_bad_sig(self): |
|---|
| 228 | d = self.publish_mdmf() |
|---|
| 229 | d.addCallback(lambda ignored: |
|---|
| 230 | corrupt(None, self._storage, 1, [1])) |
|---|
| 231 | d.addCallback(lambda ignored: |
|---|
| 232 | self._fn.check(Monitor(), verify=True)) |
|---|
| 233 | d.addCallback(self.check_bad, "test_verify_mdmf_bad_sig") |
|---|
| 234 | return d |
|---|
| 235 | |
|---|
| 236 | |
|---|
| 237 | def test_verify_mdmf_bad_encprivkey_uncheckable(self): |
|---|
| 238 | d = self.publish_mdmf() |
|---|
| 239 | d.addCallback(lambda ignored: |
|---|
| 240 | corrupt(None, self._storage, "enc_privkey", [1])) |
|---|
| 241 | d.addCallback(lambda ignored: |
|---|
| 242 | self._fn.get_readonly()) |
|---|
| 243 | d.addCallback(lambda fn: |
|---|
| 244 | fn.check(Monitor(), verify=True)) |
|---|
| 245 | d.addCallback(self.check_good, |
|---|
| 246 | "test_verify_mdmf_bad_encprivkey_uncheckable") |
|---|
| 247 | return d |
|---|
| 248 | |
|---|
| 249 | def test_verify_sdmf_empty(self): |
|---|
| 250 | d = self.publish_sdmf(b"") |
|---|
| 251 | d.addCallback(lambda ignored: self._fn.check(Monitor(), verify=True)) |
|---|
| 252 | d.addCallback(self.check_good, "test_verify_sdmf") |
|---|
| 253 | d.addCallback(flushEventualQueue) |
|---|
| 254 | return d |
|---|
| 255 | |
|---|
| 256 | def test_verify_mdmf_empty(self): |
|---|
| 257 | d = self.publish_mdmf(b"") |
|---|
| 258 | d.addCallback(lambda ignored: self._fn.check(Monitor(), verify=True)) |
|---|
| 259 | d.addCallback(self.check_good, "test_verify_mdmf") |
|---|
| 260 | d.addCallback(flushEventualQueue) |
|---|
| 261 | return d |
|---|