]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/common.py
CheckResults: pass IServer to sharemap=, but get_sharemap() returns serverids
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / common.py
1 import os, random, struct
2 from zope.interface import implements
3 from twisted.internet import defer
4 from twisted.internet.interfaces import IPullProducer
5 from twisted.python import failure
6 from twisted.application import service
7 from twisted.web.error import Error as WebError
8 from foolscap.api import flushEventualQueue, fireEventually
9 from allmydata import uri, client
10 from allmydata.introducer.server import IntroducerNode
11 from allmydata.interfaces import IMutableFileNode, IImmutableFileNode,\
12                                  NotEnoughSharesError, ICheckable, \
13                                  IMutableUploadable, SDMF_VERSION, \
14                                  MDMF_VERSION
15 from allmydata.check_results import CheckResults, CheckAndRepairResults, \
16      DeepCheckResults, DeepCheckAndRepairResults
17 from allmydata.storage_client import StubServer
18 from allmydata.mutable.layout import unpack_header
19 from allmydata.mutable.publish import MutableData
20 from allmydata.storage.mutable import MutableShareFile
21 from allmydata.util import hashutil, log, fileutil, pollmixin
22 from allmydata.util.assertutil import precondition
23 from allmydata.util.consumer import download_to_data
24 from allmydata.stats import StatsGathererService
25 from allmydata.key_generator import KeyGeneratorService
26 import allmydata.test.common_util as testutil
27 from allmydata import immutable
28
29 TEST_RSA_KEY_SIZE = 522
30
31 def flush_but_dont_ignore(res):
32     d = flushEventualQueue()
33     def _done(ignored):
34         return res
35     d.addCallback(_done)
36     return d
37
38 class DummyProducer:
39     implements(IPullProducer)
40     def resumeProducing(self):
41         pass
42
43 class FakeCHKFileNode:
44     """I provide IImmutableFileNode, but all of my data is stored in a
45     class-level dictionary."""
46     implements(IImmutableFileNode)
47
48     def __init__(self, filecap, all_contents):
49         precondition(isinstance(filecap, (uri.CHKFileURI, uri.LiteralFileURI)), filecap)
50         self.all_contents = all_contents
51         self.my_uri = filecap
52         self.storage_index = self.my_uri.get_storage_index()
53
54     def get_uri(self):
55         return self.my_uri.to_string()
56     def get_write_uri(self):
57         return None
58     def get_readonly_uri(self):
59         return self.my_uri.to_string()
60     def get_cap(self):
61         return self.my_uri
62     def get_verify_cap(self):
63         return self.my_uri.get_verify_cap()
64     def get_repair_cap(self):
65         return self.my_uri.get_verify_cap()
66     def get_storage_index(self):
67         return self.storage_index
68
69     def check(self, monitor, verify=False, add_lease=False):
70         nodeid = "\x00"*20
71         s = StubServer(nodeid)
72         r = CheckResults(self.my_uri, self.storage_index,
73                          healthy=True, recoverable=True,
74                          needs_rebalancing=False,
75                          count_shares_needed=3,
76                          count_shares_expected=10,
77                          count_shares_good=10,
78                          count_good_share_hosts=10,
79                          count_recoverable_versions=1,
80                          count_unrecoverable_versions=0,
81                          servers_responding=[nodeid],
82                          sharemap={1: [s]},
83                          count_wrong_shares=0,
84                          list_corrupt_shares=[],
85                          count_corrupt_shares=0,
86                          list_incompatible_shares=[],
87                          count_incompatible_shares=0,
88                          summary="",
89                          report=[],
90                          share_problems=[],
91                          servermap=None)
92         return defer.succeed(r)
93     def check_and_repair(self, monitor, verify=False, add_lease=False):
94         d = self.check(verify)
95         def _got(cr):
96             r = CheckAndRepairResults(self.storage_index)
97             r.pre_repair_results = r.post_repair_results = cr
98             return r
99         d.addCallback(_got)
100         return d
101
102     def is_mutable(self):
103         return False
104     def is_readonly(self):
105         return True
106     def is_unknown(self):
107         return False
108     def is_allowed_in_immutable_directory(self):
109         return True
110     def raise_error(self):
111         pass
112
113     def get_size(self):
114         if isinstance(self.my_uri, uri.LiteralFileURI):
115             return self.my_uri.get_size()
116         try:
117             data = self.all_contents[self.my_uri.to_string()]
118         except KeyError, le:
119             raise NotEnoughSharesError(le, 0, 3)
120         return len(data)
121     def get_current_size(self):
122         return defer.succeed(self.get_size())
123
124     def read(self, consumer, offset=0, size=None):
125         # we don't bother to call registerProducer/unregisterProducer,
126         # because it's a hassle to write a dummy Producer that does the right
127         # thing (we have to make sure that DummyProducer.resumeProducing
128         # writes the data into the consumer immediately, otherwise it will
129         # loop forever).
130
131         d = defer.succeed(None)
132         d.addCallback(self._read, consumer, offset, size)
133         return d
134
135     def _read(self, ignored, consumer, offset, size):
136         if isinstance(self.my_uri, uri.LiteralFileURI):
137             data = self.my_uri.data
138         else:
139             if self.my_uri.to_string() not in self.all_contents:
140                 raise NotEnoughSharesError(None, 0, 3)
141             data = self.all_contents[self.my_uri.to_string()]
142         start = offset
143         if size is not None:
144             end = offset + size
145         else:
146             end = len(data)
147         consumer.write(data[start:end])
148         return consumer
149
150
151     def get_best_readable_version(self):
152         return defer.succeed(self)
153
154
155     def download_to_data(self):
156         return download_to_data(self)
157
158
159     download_best_version = download_to_data
160
161
162     def get_size_of_best_version(self):
163         return defer.succeed(self.get_size)
164
165
166 def make_chk_file_cap(size):
167     return uri.CHKFileURI(key=os.urandom(16),
168                           uri_extension_hash=os.urandom(32),
169                           needed_shares=3,
170                           total_shares=10,
171                           size=size)
172 def make_chk_file_uri(size):
173     return make_chk_file_cap(size).to_string()
174
175 def create_chk_filenode(contents, all_contents):
176     filecap = make_chk_file_cap(len(contents))
177     n = FakeCHKFileNode(filecap, all_contents)
178     all_contents[filecap.to_string()] = contents
179     return n
180
181
182 class FakeMutableFileNode:
183     """I provide IMutableFileNode, but all of my data is stored in a
184     class-level dictionary."""
185
186     implements(IMutableFileNode, ICheckable)
187     MUTABLE_SIZELIMIT = 10000
188
189     def __init__(self, storage_broker, secret_holder,
190                  default_encoding_parameters, history, all_contents):
191         self.all_contents = all_contents
192         self.file_types = {} # storage index => MDMF_VERSION or SDMF_VERSION
193         self.init_from_cap(make_mutable_file_cap())
194         self._k = default_encoding_parameters['k']
195         self._segsize = default_encoding_parameters['max_segment_size']
196     def create(self, contents, key_generator=None, keysize=None,
197                version=SDMF_VERSION):
198         if version == MDMF_VERSION and \
199             isinstance(self.my_uri, (uri.ReadonlySSKFileURI,
200                                  uri.WriteableSSKFileURI)):
201             self.init_from_cap(make_mdmf_mutable_file_cap())
202         self.file_types[self.storage_index] = version
203         initial_contents = self._get_initial_contents(contents)
204         data = initial_contents.read(initial_contents.get_size())
205         data = "".join(data)
206         self.all_contents[self.storage_index] = data
207         return defer.succeed(self)
208     def _get_initial_contents(self, contents):
209         if contents is None:
210             return MutableData("")
211
212         if IMutableUploadable.providedBy(contents):
213             return contents
214
215         assert callable(contents), "%s should be callable, not %s" % \
216                (contents, type(contents))
217         return contents(self)
218     def init_from_cap(self, filecap):
219         assert isinstance(filecap, (uri.WriteableSSKFileURI,
220                                     uri.ReadonlySSKFileURI,
221                                     uri.WriteableMDMFFileURI,
222                                     uri.ReadonlyMDMFFileURI))
223         self.my_uri = filecap
224         self.storage_index = self.my_uri.get_storage_index()
225         if isinstance(filecap, (uri.WriteableMDMFFileURI,
226                                 uri.ReadonlyMDMFFileURI)):
227             self.file_types[self.storage_index] = MDMF_VERSION
228
229         else:
230             self.file_types[self.storage_index] = SDMF_VERSION
231
232         return self
233     def get_cap(self):
234         return self.my_uri
235     def get_readcap(self):
236         return self.my_uri.get_readonly()
237     def get_uri(self):
238         return self.my_uri.to_string()
239     def get_write_uri(self):
240         if self.is_readonly():
241             return None
242         return self.my_uri.to_string()
243     def get_readonly(self):
244         return self.my_uri.get_readonly()
245     def get_readonly_uri(self):
246         return self.my_uri.get_readonly().to_string()
247     def get_verify_cap(self):
248         return self.my_uri.get_verify_cap()
249     def get_repair_cap(self):
250         if self.my_uri.is_readonly():
251             return None
252         return self.my_uri
253     def is_readonly(self):
254         return self.my_uri.is_readonly()
255     def is_mutable(self):
256         return self.my_uri.is_mutable()
257     def is_unknown(self):
258         return False
259     def is_allowed_in_immutable_directory(self):
260         return not self.my_uri.is_mutable()
261     def raise_error(self):
262         pass
263     def get_writekey(self):
264         return "\x00"*16
265     def get_size(self):
266         return len(self.all_contents[self.storage_index])
267     def get_current_size(self):
268         return self.get_size_of_best_version()
269     def get_size_of_best_version(self):
270         return defer.succeed(len(self.all_contents[self.storage_index]))
271
272     def get_storage_index(self):
273         return self.storage_index
274
275     def get_servermap(self, mode):
276         return defer.succeed(None)
277
278     def get_version(self):
279         assert self.storage_index in self.file_types
280         return self.file_types[self.storage_index]
281
282     def check(self, monitor, verify=False, add_lease=False):
283         nodeid = "\x00"*20
284         s = StubServer(nodeid)
285         r = CheckResults(self.my_uri, self.storage_index,
286                          healthy=True, recoverable=True,
287                          needs_rebalancing=False,
288                          count_shares_needed=3,
289                          count_shares_expected=10,
290                          count_shares_good=10,
291                          count_good_share_hosts=10,
292                          count_recoverable_versions=1,
293                          count_unrecoverable_versions=0,
294                          servers_responding=[nodeid],
295                          sharemap={"seq1-abcd-sh0": [s]},
296                          count_wrong_shares=0,
297                          list_corrupt_shares=[],
298                          count_corrupt_shares=0,
299                          list_incompatible_shares=[],
300                          count_incompatible_shares=0,
301                          summary="",
302                          report=[],
303                          share_problems=[],
304                          servermap=None)
305         return defer.succeed(r)
306
307     def check_and_repair(self, monitor, verify=False, add_lease=False):
308         d = self.check(verify)
309         def _got(cr):
310             r = CheckAndRepairResults(self.storage_index)
311             r.pre_repair_results = r.post_repair_results = cr
312             return r
313         d.addCallback(_got)
314         return d
315
316     def deep_check(self, verify=False, add_lease=False):
317         d = self.check(verify)
318         def _done(r):
319             dr = DeepCheckResults(self.storage_index)
320             dr.add_check(r, [])
321             return dr
322         d.addCallback(_done)
323         return d
324
325     def deep_check_and_repair(self, verify=False, add_lease=False):
326         d = self.check_and_repair(verify)
327         def _done(r):
328             dr = DeepCheckAndRepairResults(self.storage_index)
329             dr.add_check(r, [])
330             return dr
331         d.addCallback(_done)
332         return d
333
334     def download_best_version(self):
335         return defer.succeed(self._download_best_version())
336
337
338     def _download_best_version(self, ignored=None):
339         if isinstance(self.my_uri, uri.LiteralFileURI):
340             return self.my_uri.data
341         if self.storage_index not in self.all_contents:
342             raise NotEnoughSharesError(None, 0, 3)
343         return self.all_contents[self.storage_index]
344
345
346     def overwrite(self, new_contents):
347         assert not self.is_readonly()
348         new_data = new_contents.read(new_contents.get_size())
349         new_data = "".join(new_data)
350         self.all_contents[self.storage_index] = new_data
351         return defer.succeed(None)
352     def modify(self, modifier):
353         # this does not implement FileTooLargeError, but the real one does
354         return defer.maybeDeferred(self._modify, modifier)
355     def _modify(self, modifier):
356         assert not self.is_readonly()
357         old_contents = self.all_contents[self.storage_index]
358         new_data = modifier(old_contents, None, True)
359         self.all_contents[self.storage_index] = new_data
360         return None
361
362     # As actually implemented, MutableFilenode and MutableFileVersion
363     # are distinct. However, nothing in the webapi uses (yet) that
364     # distinction -- it just uses the unified download interface
365     # provided by get_best_readable_version and read. When we start
366     # doing cooler things like LDMF, we will want to revise this code to
367     # be less simplistic.
368     def get_best_readable_version(self):
369         return defer.succeed(self)
370
371
372     def get_best_mutable_version(self):
373         return defer.succeed(self)
374
375     # Ditto for this, which is an implementation of IWriteable.
376     # XXX: Declare that the same is implemented.
377     def update(self, data, offset):
378         assert not self.is_readonly()
379         def modifier(old, servermap, first_time):
380             new = old[:offset] + "".join(data.read(data.get_size()))
381             new += old[len(new):]
382             return new
383         return self.modify(modifier)
384
385
386     def read(self, consumer, offset=0, size=None):
387         data = self._download_best_version()
388         if size:
389             data = data[offset:offset+size]
390         consumer.write(data)
391         return defer.succeed(consumer)
392
393
394 def make_mutable_file_cap():
395     return uri.WriteableSSKFileURI(writekey=os.urandom(16),
396                                    fingerprint=os.urandom(32))
397
398 def make_mdmf_mutable_file_cap():
399     return uri.WriteableMDMFFileURI(writekey=os.urandom(16),
400                                    fingerprint=os.urandom(32))
401
402 def make_mutable_file_uri(mdmf=False):
403     if mdmf:
404         uri = make_mdmf_mutable_file_cap()
405     else:
406         uri = make_mutable_file_cap()
407
408     return uri.to_string()
409
410 def make_verifier_uri():
411     return uri.SSKVerifierURI(storage_index=os.urandom(16),
412                               fingerprint=os.urandom(32)).to_string()
413
414 def create_mutable_filenode(contents, mdmf=False, all_contents=None):
415     # XXX: All of these arguments are kind of stupid. 
416     if mdmf:
417         cap = make_mdmf_mutable_file_cap()
418     else:
419         cap = make_mutable_file_cap()
420
421     encoding_params = {}
422     encoding_params['k'] = 3
423     encoding_params['max_segment_size'] = 128*1024
424
425     filenode = FakeMutableFileNode(None, None, encoding_params, None,
426                                    all_contents)
427     filenode.init_from_cap(cap)
428     if mdmf:
429         filenode.create(MutableData(contents), version=MDMF_VERSION)
430     else:
431         filenode.create(MutableData(contents), version=SDMF_VERSION)
432     return filenode
433
434
435 class LoggingServiceParent(service.MultiService):
436     def log(self, *args, **kwargs):
437         return log.msg(*args, **kwargs)
438
439 class SystemTestMixin(pollmixin.PollMixin, testutil.StallMixin):
440
441     # SystemTestMixin tests tend to be a lot of work, and we have a few
442     # buildslaves that are pretty slow, and every once in a while these tests
443     # run up against the default 120 second timeout. So increase the default
444     # timeout. Individual test cases can override this, of course.
445     timeout = 300
446
447     def setUp(self):
448         self.sparent = service.MultiService()
449         self.sparent.startService()
450
451         self.stats_gatherer = None
452         self.stats_gatherer_furl = None
453         self.key_generator_svc = None
454         self.key_generator_furl = None
455
456     def tearDown(self):
457         log.msg("shutting down SystemTest services")
458         d = self.sparent.stopService()
459         d.addBoth(flush_but_dont_ignore)
460         return d
461
462     def getdir(self, subdir):
463         return os.path.join(self.basedir, subdir)
464
465     def add_service(self, s):
466         s.setServiceParent(self.sparent)
467         return s
468
469     def set_up_nodes(self, NUMCLIENTS=5,
470                      use_stats_gatherer=False, use_key_generator=False):
471         self.numclients = NUMCLIENTS
472         iv_dir = self.getdir("introducer")
473         if not os.path.isdir(iv_dir):
474             fileutil.make_dirs(iv_dir)
475             fileutil.write(os.path.join(iv_dir, 'tahoe.cfg'), \
476                                "[node]\n" + \
477                                "web.port = tcp:0:interface=127.0.0.1\n")
478             if SYSTEM_TEST_CERTS:
479                 os.mkdir(os.path.join(iv_dir, "private"))
480                 f = open(os.path.join(iv_dir, "private", "node.pem"), "w")
481                 f.write(SYSTEM_TEST_CERTS[0])
482                 f.close()
483         iv = IntroducerNode(basedir=iv_dir)
484         self.introducer = self.add_service(iv)
485         d = self.introducer.when_tub_ready()
486         d.addCallback(self._get_introducer_web)
487         if use_stats_gatherer:
488             d.addCallback(self._set_up_stats_gatherer)
489         if use_key_generator:
490             d.addCallback(self._set_up_key_generator)
491         d.addCallback(self._set_up_nodes_2)
492         if use_stats_gatherer:
493             d.addCallback(self._grab_stats)
494         return d
495
496     def _get_introducer_web(self, res):
497         f = open(os.path.join(self.getdir("introducer"), "node.url"), "r")
498         self.introweb_url = f.read().strip()
499         f.close()
500
501     def _set_up_stats_gatherer(self, res):
502         statsdir = self.getdir("stats_gatherer")
503         fileutil.make_dirs(statsdir)
504         self.stats_gatherer_svc = StatsGathererService(statsdir)
505         self.stats_gatherer = self.stats_gatherer_svc.stats_gatherer
506         self.add_service(self.stats_gatherer_svc)
507
508         d = fireEventually()
509         sgf = os.path.join(statsdir, 'stats_gatherer.furl')
510         def check_for_furl():
511             return os.path.exists(sgf)
512         d.addCallback(lambda junk: self.poll(check_for_furl, timeout=30))
513         def get_furl(junk):
514             self.stats_gatherer_furl = file(sgf, 'rb').read().strip()
515         d.addCallback(get_furl)
516         return d
517
518     def _set_up_key_generator(self, res):
519         kgsdir = self.getdir("key_generator")
520         fileutil.make_dirs(kgsdir)
521
522         self.key_generator_svc = KeyGeneratorService(kgsdir,
523                                                      display_furl=False,
524                                                      default_key_size=TEST_RSA_KEY_SIZE)
525         self.key_generator_svc.key_generator.pool_size = 4
526         self.key_generator_svc.key_generator.pool_refresh_delay = 60
527         self.add_service(self.key_generator_svc)
528
529         d = fireEventually()
530         def check_for_furl():
531             return os.path.exists(os.path.join(kgsdir, 'key_generator.furl'))
532         d.addCallback(lambda junk: self.poll(check_for_furl, timeout=30))
533         def get_furl(junk):
534             kgf = os.path.join(kgsdir, 'key_generator.furl')
535             self.key_generator_furl = file(kgf, 'rb').read().strip()
536         d.addCallback(get_furl)
537         return d
538
539     def _set_up_nodes_2(self, res):
540         q = self.introducer
541         self.introducer_furl = q.introducer_url
542         self.clients = []
543         basedirs = []
544         for i in range(self.numclients):
545             basedir = self.getdir("client%d" % i)
546             basedirs.append(basedir)
547             fileutil.make_dirs(os.path.join(basedir, "private"))
548             if len(SYSTEM_TEST_CERTS) > (i+1):
549                 f = open(os.path.join(basedir, "private", "node.pem"), "w")
550                 f.write(SYSTEM_TEST_CERTS[i+1])
551                 f.close()
552
553             config = "[client]\n"
554             config += "introducer.furl = %s\n" % self.introducer_furl
555             if self.stats_gatherer_furl:
556                 config += "stats_gatherer.furl = %s\n" % self.stats_gatherer_furl
557
558             if i == 0:
559                 # clients[0] runs a webserver and a helper, no key_generator
560                 config += "[node]\n"
561                 config += "web.port = tcp:0:interface=127.0.0.1\n"
562                 config += "timeout.keepalive = 600\n"
563                 config += "[helper]\n"
564                 config += "enabled = True\n"
565             if i == 3:
566                 # clients[3] runs a webserver and uses a helper, uses
567                 # key_generator
568                 if self.key_generator_furl:
569                     config += "key_generator.furl = %s\n" % self.key_generator_furl
570                 config += "[node]\n"
571                 config += "web.port = tcp:0:interface=127.0.0.1\n"
572                 config += "timeout.disconnect = 1800\n"
573
574             fileutil.write(os.path.join(basedir, 'tahoe.cfg'), config)
575
576         # give subclasses a chance to append lines to the node's tahoe.cfg
577         # files before they are launched.
578         self._set_up_nodes_extra_config()
579
580         # start clients[0], wait for it's tub to be ready (at which point it
581         # will have registered the helper furl).
582         c = self.add_service(client.Client(basedir=basedirs[0]))
583         self.clients.append(c)
584         c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE)
585         d = c.when_tub_ready()
586         def _ready(res):
587             f = open(os.path.join(basedirs[0],"private","helper.furl"), "r")
588             helper_furl = f.read()
589             f.close()
590             self.helper_furl = helper_furl
591             if self.numclients >= 4:
592                 f = open(os.path.join(basedirs[3], 'tahoe.cfg'), 'ab+')
593                 f.write(
594                       "[client]\n"
595                       "helper.furl = %s\n" % helper_furl)
596                 f.close()
597
598             # this starts the rest of the clients
599             for i in range(1, self.numclients):
600                 c = self.add_service(client.Client(basedir=basedirs[i]))
601                 self.clients.append(c)
602                 c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE)
603             log.msg("STARTING")
604             return self.wait_for_connections()
605         d.addCallback(_ready)
606         def _connected(res):
607             log.msg("CONNECTED")
608             # now find out where the web port was
609             self.webish_url = self.clients[0].getServiceNamed("webish").getURL()
610             if self.numclients >=4:
611                 # and the helper-using webport
612                 self.helper_webish_url = self.clients[3].getServiceNamed("webish").getURL()
613         d.addCallback(_connected)
614         return d
615
616     def _set_up_nodes_extra_config(self):
617         # for overriding by subclasses
618         pass
619
620     def _grab_stats(self, res):
621         d = self.stats_gatherer.poll()
622         return d
623
624     def bounce_client(self, num):
625         c = self.clients[num]
626         d = c.disownServiceParent()
627         # I think windows requires a moment to let the connection really stop
628         # and the port number made available for re-use. TODO: examine the
629         # behavior, see if this is really the problem, see if we can do
630         # better than blindly waiting for a second.
631         d.addCallback(self.stall, 1.0)
632         def _stopped(res):
633             new_c = client.Client(basedir=self.getdir("client%d" % num))
634             self.clients[num] = new_c
635             new_c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE)
636             self.add_service(new_c)
637             return new_c.when_tub_ready()
638         d.addCallback(_stopped)
639         d.addCallback(lambda res: self.wait_for_connections())
640         def _maybe_get_webport(res):
641             if num == 0:
642                 # now find out where the web port was
643                 self.webish_url = self.clients[0].getServiceNamed("webish").getURL()
644         d.addCallback(_maybe_get_webport)
645         return d
646
647     def add_extra_node(self, client_num, helper_furl=None,
648                        add_to_sparent=False):
649         # usually this node is *not* parented to our self.sparent, so we can
650         # shut it down separately from the rest, to exercise the
651         # connection-lost code
652         basedir = self.getdir("client%d" % client_num)
653         if not os.path.isdir(basedir):
654             fileutil.make_dirs(basedir)
655         config = "[client]\n"
656         config += "introducer.furl = %s\n" % self.introducer_furl
657         if helper_furl:
658             config += "helper.furl = %s\n" % helper_furl
659         fileutil.write(os.path.join(basedir, 'tahoe.cfg'), config)
660
661         c = client.Client(basedir=basedir)
662         self.clients.append(c)
663         c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE)
664         self.numclients += 1
665         if add_to_sparent:
666             c.setServiceParent(self.sparent)
667         else:
668             c.startService()
669         d = self.wait_for_connections()
670         d.addCallback(lambda res: c)
671         return d
672
673     def _check_connections(self):
674         for c in self.clients:
675             if not c.connected_to_introducer():
676                 return False
677             sb = c.get_storage_broker()
678             if len(sb.get_connected_servers()) != self.numclients:
679                 return False
680         return True
681
682     def wait_for_connections(self, ignored=None):
683         # TODO: replace this with something that takes a list of peerids and
684         # fires when they've all been heard from, instead of using a count
685         # and a threshold
686         return self.poll(self._check_connections, timeout=200)
687
688
689 # our system test uses the same Tub certificates each time, to avoid the
690 # overhead of key generation
691 SYSTEM_TEST_CERTS = [
692 """-----BEGIN CERTIFICATE-----
693 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
694 aW5neTAeFw0wODA3MjUyMjQyMDVaFw0wOTA3MjUyMjQyMDVaMBcxFTATBgNVBAMU
695 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxHCWajrR
696 2h/iurw8k93m8WUdE3xypJiiAITw7GkKlKbCLD+dEce2MXwVVYca0n/MZZsj89Cu
697 Ko0lLjksMseoSDoj98iEmVpaY5mc2ntpQ+FXdoEmPP234XRWEg2HQ+EaK6+WkGQg
698 DDXQvFJCVCQk/n1MdAwZZ6vqf2ITzSuD44kCAwEAATANBgkqhkiG9w0BAQQFAAOB
699 gQBn6qPKGdFjWJy7sOOTUFfm/THhHQqAh1pBDLkjR+OtzuobCoP8n8J1LNG3Yxds
700 Jj7NWQL7X5TfOlfoi7e9jK0ujGgWh3yYU6PnHzJLkDiDT3LCSywQuGXCjh0tOStS
701 2gaCmmAK2cfxSStKzNcewl2Zs8wHMygq8TLFoZ6ozN1+xQ==
702 -----END CERTIFICATE-----
703 -----BEGIN RSA PRIVATE KEY-----
704 MIICXQIBAAKBgQDEcJZqOtHaH+K6vDyT3ebxZR0TfHKkmKIAhPDsaQqUpsIsP50R
705 x7YxfBVVhxrSf8xlmyPz0K4qjSUuOSwyx6hIOiP3yISZWlpjmZzae2lD4Vd2gSY8
706 /bfhdFYSDYdD4Rorr5aQZCAMNdC8UkJUJCT+fUx0DBlnq+p/YhPNK4PjiQIDAQAB
707 AoGAZyDMdrymiyMOPwavrtlicvyohSBid3MCKc+hRBvpSB0790r2RO1aAySndp1V
708 QYmCXx1RhKDbrs8m49t0Dryu5T+sQrFl0E3usAP3vvXWeh4jwJ9GyiRWy4xOEuEQ
709 3ewjbEItHqA/bRJF0TNtbOmZTDC7v9FRPf2bTAyFfTZep5kCQQD33q1RA8WUYtmQ
710 IArgHqt69i421lpXlOgqotFHwTx4FiGgVzDQCDuXU6txB9EeKRM340poissav/n6
711 bkLZ7/VDAkEAyuIPkeI59sE5NnmW+N47NbCfdM1Smy1YxZpv942EmP9Veub5N0dw
712 iK5bLAgEguUIjpTsh3BRmsE9Xd+ItmnRQwJBAMZhbg19G1EbnE0BmDKv2UbcaThy
713 bnPSNc6J6T2opqDl9ZvCrMqTDD6dNIWOYAvni/4a556sFsoeBBAu10peBskCQE6S
714 cB86cuJagLLVMh/dySaI6ahNoFFSpY+ZuQUxfInYUR2Q+DFtbGqyw8JwtHaRBthZ
715 WqU1XZVGg2KooISsxIsCQQD1PS7//xHLumBb0jnpL7n6W8gmiTyzblT+0otaCisP
716 fN6rTlwV1o8VsOUAz0rmKO5RArCbkmb01WtMgPCDBYkk
717 -----END RSA PRIVATE KEY-----
718 """, # 0
719 """-----BEGIN CERTIFICATE-----
720 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
721 aW5neTAeFw0wODA3MjUyMjQyMDVaFw0wOTA3MjUyMjQyMDVaMBcxFTATBgNVBAMU
722 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAs9CALdmW
723 kJ6r0KPSLdGCA8rzQKxWayrMckT22ZtbRv3aw6VA96dWclpY+T2maV0LrAzmMSL8
724 n61ydJHM33iYDOyWbwHWN45XCjY/e20PL54XUl/DmbBHEhQVQLIfCldcRcnWEfoO
725 iOhDJfWpDO1dmP/aOYLdkZCZvBtPAfyUqRcCAwEAATANBgkqhkiG9w0BAQQFAAOB
726 gQAN9eaCREkzzk4yPIaWYkWHg3Igs1vnOR/iDw3OjyxO/xJFP2lkA2WtrwL2RTRq
727 dxA8gwdPyrWgdiZElwZH8mzTJ4OdUXLSMclLOg9kvH6gtSvhLztfEDwDP1wRhikh
728 OeWWu2GIC+uqFCI1ftoGgU+aIa6yrHswf66rrQvBSSvJPQ==
729 -----END CERTIFICATE-----
730 -----BEGIN RSA PRIVATE KEY-----
731 MIICXQIBAAKBgQCz0IAt2ZaQnqvQo9It0YIDyvNArFZrKsxyRPbZm1tG/drDpUD3
732 p1ZyWlj5PaZpXQusDOYxIvyfrXJ0kczfeJgM7JZvAdY3jlcKNj97bQ8vnhdSX8OZ
733 sEcSFBVAsh8KV1xFydYR+g6I6EMl9akM7V2Y/9o5gt2RkJm8G08B/JSpFwIDAQAB
734 AoGBAIUy5zCPpSP+FeJY6CG+t6Pdm/IFd4KtUoM3KPCrT6M3+uzApm6Ny9Crsor2
735 qyYTocjSSVaOxzn1fvpw4qWLrH1veUf8ozMs8Z0VuPHD1GYUGjOXaBPXb5o1fQL9
736 h7pS5/HrDDPN6wwDNTsxRf/fP58CnfwQUhwdoxcx8TnVmDQxAkEA6N3jBXt/Lh0z
737 UbXHhv3QBOcqLZA2I4tY7wQzvUvKvVmCJoW1tfhBdYQWeQv0jzjL5PzrrNY8hC4l
738 8+sFM3h5TwJBAMWtbFIEZfRSG1JhHK3evYHDTZnr/j+CdoWuhzP5RkjkIKsiLEH7
739 2ZhA7CdFQLZF14oXy+g1uVCzzfB2WELtUbkCQQDKrb1XWzrBlzbAipfkXWs9qTmj
740 uJ32Z+V6+0xRGPOXxJ0sDDqw7CeFMfchWg98zLFiV+SEZV78qPHtkAPR3ayvAkB+
741 hUMhM4N13t9x2IoclsXAOhp++9bdG0l0woHyuAdOPATUw6iECwf4NQVxFRgYEZek
742 4Ro3Y7taddrHn1dabr6xAkAic47OoLOROYLpljmJJO0eRe3Z5IFe+0D2LfhAW3LQ
743 JU+oGq5pCjfnoaDElRRZn0+GmunnWeQEYKoflTi/lI9d
744 -----END RSA PRIVATE KEY-----
745 """, # 1
746 """-----BEGIN CERTIFICATE-----
747 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
748 aW5neTAeFw0wODA3MjUyMjQyMDZaFw0wOTA3MjUyMjQyMDZaMBcxFTATBgNVBAMU
749 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsxG7LTrz
750 DF+9wegOR/BRJhjSumPUbYQnNAUKtPraFsGjAJILP44AHdnHt1MONLgTeX1ynapo
751 q6O/q5cdKtBB7uEh7FpkLCCwpZt/m0y79cynn8AmWoQVgl8oS0567UmPeJnTzFPv
752 dmT5dlaQALeX5YGceAsEvhmAsdOMttaor38CAwEAATANBgkqhkiG9w0BAQQFAAOB
753 gQA345rxotfvh2kfgrmRzAyGewVBV4r23Go30GSZir8X2GoH3qKNwO4SekAohuSw
754 AiXzLUbwIdSRSqaLFxSC7Duqc9eIeFDAWjeEmpfFLBNiw3K8SLA00QrHCUXnECTD
755 b/Kk6OGuvPOiuuONVjEuEcRdCH3/Li30D0AhJaMynjhQJQ==
756 -----END CERTIFICATE-----
757 -----BEGIN RSA PRIVATE KEY-----
758 MIICXQIBAAKBgQCzEbstOvMMX73B6A5H8FEmGNK6Y9RthCc0BQq0+toWwaMAkgs/
759 jgAd2ce3Uw40uBN5fXKdqmiro7+rlx0q0EHu4SHsWmQsILClm3+bTLv1zKefwCZa
760 hBWCXyhLTnrtSY94mdPMU+92ZPl2VpAAt5flgZx4CwS+GYCx04y21qivfwIDAQAB
761 AoGBAIlhFg/aRPL+VM9539LzHN60dp8GzceDdqwjHhbAySZiQlLCuJx2rcI4/U65
762 CpIJku9G/fLV9N2RkA/trDPXeGyqCTJfnNzyZcvvMscRMFqSGyc21Y0a+GS8bIxt
763 1R2B18epSVMsWSWWMypeEgsfv29LV7oSWG8UKaqQ9+0h63DhAkEA4i2L/rori/Fb
764 wpIBfA+xbXL/GmWR7xPW+3nG3LdLQpVzxz4rIsmtO9hIXzvYpcufQbwgVACyMmRf
765 TMABeSDM7wJBAMquEdTaVXjGfH0EJ7z95Ys2rYTiCXjBfyEOi6RXXReqV9SXNKlN
766 aKsO22zYecpkAjY1EdUdXWP/mNVEybjpZnECQQCcuh0JPS5RwcTo9c2rjyBOjGIz
767 g3B1b5UIG2FurmCrWe6pgO3ZJFEzZ/L2cvz0Hj5UCa2JKBZTDvRutZoPumfnAkAb
768 nSW+y1Rz1Q8m9Ub4v9rjYbq4bRd/RVWtyk6KQIDldYbr5wH8wxgsniSVKtVFFuUa
769 P5bDY3HS6wMGo42cTOhxAkAcdweQSQ3j7mfc5vh71HeAC1v/VAKGehGOUdeEIQNl
770 Sb2WuzpZkbfsrVzW6MdlgY6eE7ufRswhDPLWPC8MP0d1
771 -----END RSA PRIVATE KEY-----
772 """, # 2
773 """-----BEGIN CERTIFICATE-----
774 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
775 aW5neTAeFw0wODA3MjUyMjQyMDZaFw0wOTA3MjUyMjQyMDZaMBcxFTATBgNVBAMU
776 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxnH+pbOS
777 qlJlsHpKUQtV0oN1Mv+ESG+yUDxStFFGjkJv/UIRzpxqFqY/6nJ3D03kZsDdcXyi
778 CfV9hPYQaVNMn6z+puPmIagfBQ0aOyuI+nUhCttZIYD9071BjW5bCMX5NZWL/CZm
779 E0HdAZ77H6UrRckJ7VR8wAFpihBxD5WliZcCAwEAATANBgkqhkiG9w0BAQQFAAOB
780 gQAwXqY1Sjvp9JSTHKklu7s0T6YmH/BKSXrHpS2xO69svK+ze5/+5td3jPn4Qe50
781 xwRNZSFmSLuJLfCO32QJSJTB7Vs5D3dNTZ2i8umsaodm97t8hit7L75nXRGHKH//
782 xDVWAFB9sSgCQyPMRkL4wB4YSfRhoSKVwMvaz+XRZDUU0A==
783 -----END CERTIFICATE-----
784 -----BEGIN RSA PRIVATE KEY-----
785 MIICXAIBAAKBgQDGcf6ls5KqUmWwekpRC1XSg3Uy/4RIb7JQPFK0UUaOQm/9QhHO
786 nGoWpj/qcncPTeRmwN1xfKIJ9X2E9hBpU0yfrP6m4+YhqB8FDRo7K4j6dSEK21kh
787 gP3TvUGNblsIxfk1lYv8JmYTQd0BnvsfpStFyQntVHzAAWmKEHEPlaWJlwIDAQAB
788 AoGAdHNMlXwtItm7ZrY8ihZ2xFP0IHsk60TwhHkBp2LSXoTKJvnwbSgIcUYZ18BX
789 8Zkp4MpoqEIU7HcssyuaMdR572huV2w0D/2gYJQLQ5JapaR3hMox3YG4wjXasN1U
790 1iZt7JkhKlOy+ElL5T9mKTE1jDsX2RAv4WALzMpYFo7vs4ECQQDxqrPaqRQ5uYS/
791 ejmIk05nM3Q1zmoLtMDrfRqrjBhaf/W3hqGihiqN2kL3PIIYcxSRWiyNlYXjElsR
792 2sllBTe3AkEA0jcMHVThwKt1+Ce5VcE7N6hFfbsgISTjfJ+Q3K2NkvJkmtE8ZRX5
793 XprssnPN8owkfF5yuKbcSZL3uvaaSGN9IQJAfTVnN9wwOXQwHhDSbDt9/KRBCnum
794 n+gHqDrKLaVJHOJ9SZf8eLswoww5c+UqtkYxmtlwie61Tp+9BXQosilQ4wJBAIZ1
795 XVNZmriBM4jR59L5MOZtxF0ilu98R+HLsn3kqLyIPF9mXCoQPxwLHkEan213xFKk
796 mt6PJDIPRlOZLqAEuuECQFQMCrn0VUwPg8E40pxMwgMETvVflPs/oZK1Iu+b7+WY
797 vBptAyhMu31fHQFnJpiUOyHqSZnOZyEn1Qu2lszNvUg=
798 -----END RSA PRIVATE KEY-----
799 """, # 3
800 """-----BEGIN CERTIFICATE-----
801 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
802 aW5neTAeFw0wODA3MjUyMjQyMDZaFw0wOTA3MjUyMjQyMDZaMBcxFTATBgNVBAMU
803 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAnjiOwipn
804 jigDuNMfNG/tBJhPwYUHhSbQdvrTubhsxw1oOq5XpNqUwRtC8hktOKM3hghyqExP
805 62EOi0aJBkRhtwtPSLBCINptArZLfkog/nTIqVv4eLEzJ19nTi/llHHWKcgA6XTI
806 sU/snUhGlySA3RpETvXqIJTauQRZz0kToSUCAwEAATANBgkqhkiG9w0BAQQFAAOB
807 gQCQ+u/CsX5WC5m0cLrpyIS6qZa62lrB3mj9H1aIQhisT5kRsMz3FJ1aOaS8zPRz
808 w0jhyRmamCcSsWf5WK539iOtsXbKMdAyjNtkQO3g+fnsLgmznAjjst24jfr+XU59
809 0amiy1U6TY93gtEBZHtiLldPdUMsTuFbBlqbcMBQ50x9rA==
810 -----END CERTIFICATE-----
811 -----BEGIN RSA PRIVATE KEY-----
812 MIICXAIBAAKBgQCeOI7CKmeOKAO40x80b+0EmE/BhQeFJtB2+tO5uGzHDWg6rlek
813 2pTBG0LyGS04ozeGCHKoTE/rYQ6LRokGRGG3C09IsEIg2m0Ctkt+SiD+dMipW/h4
814 sTMnX2dOL+WUcdYpyADpdMixT+ydSEaXJIDdGkRO9eoglNq5BFnPSROhJQIDAQAB
815 AoGAAPrst3s3xQOucjismtCOsVaYN+SxFTwWUoZfRWlFEz6cBLELzfOktEWM9p79
816 TrqEH4px22UNobGqO2amdql5yXwEFVhYQkRB8uDA8uVaqpL8NLWTGPRXxZ2DSU+n
817 7/FLf/TWT3ti/ZtXaPVRj6E2/Mq9AVEVOjUYzkNjM02OxcECQQDKEqmPbdZq2URU
818 7RbUxkq5aTp8nzAgbpUsgBGQ9PDAymhj60BDEP0q28Ssa7tU70pRnQ3AZs9txgmL
819 kK2g97FNAkEAyHH9cIb6qXOAJPIr/xamFGr5uuYw9TJPz/hfVkVimW/aZnBB+e6Q
820 oALJBDKJWeYPzdNbouJYg8MeU0qWdZ5DOQJADUk+1sxc/bd9U6wnBSRog1pU2x7I
821 VkmPC1b8ULCaJ8LnLDKqjf5O9wNuIfwPXB1DoKwX3F+mIcyUkhWYJO5EPQJAUj5D
822 KMqZSrGzYHVlC/M1Daee88rDR7fu+3wDUhiCDkbQq7tftrbl7GF4LRq3NIWq8l7I
823 eJq6isWiSbaO6Y+YMQJBAJFBpVhlY5Px2BX5+Hsfq6dSP3sVVc0eHkdsoZFFxq37
824 fksL/q2vlPczvBihgcxt+UzW/UrNkelOuX3i57PDvFs=
825 -----END RSA PRIVATE KEY-----
826 """, # 4
827 """-----BEGIN CERTIFICATE-----
828 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
829 aW5neTAeFw0wODA3MjUyMjQyMDZaFw0wOTA3MjUyMjQyMDZaMBcxFTATBgNVBAMU
830 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsCQuudDF
831 zgmY5tDpT0TkUo8fpJ5JcvgCkLFpSDD8REpXhLFkHWhTmTj3CAxfv4lA3sQzHZxe
832 4S9YCb5c/VTbFEdgwc/wlxMmJiz2jYghdmWPBb8pBEk31YihIhC+u4kex6gJBH5y
833 ixiZ3PPRRMaOBBo+ZfM50XIyWbFOOM/7FwcCAwEAATANBgkqhkiG9w0BAQQFAAOB
834 gQB4cFURaiiUx6n8eS4j4Vxrii5PtsaNEI4acANFSYknGd0xTP4vnmoivNmo5fWE
835 Q4hYtGezNu4a9MnNhcQmI20KzXmvhLJtkwWCgGOVJtMem8hDWXSALV1Ih8hmVkGS
836 CI1elfr9eyguunGp9eMMQfKhWH52WHFA0NYa0Kpv5BY33A==
837 -----END CERTIFICATE-----
838 -----BEGIN RSA PRIVATE KEY-----
839 MIICWwIBAAKBgQCwJC650MXOCZjm0OlPRORSjx+knkly+AKQsWlIMPxESleEsWQd
840 aFOZOPcIDF+/iUDexDMdnF7hL1gJvlz9VNsUR2DBz/CXEyYmLPaNiCF2ZY8FvykE
841 STfViKEiEL67iR7HqAkEfnKLGJnc89FExo4EGj5l8znRcjJZsU44z/sXBwIDAQAB
842 AoGABA7xXKqoxBSIh1js5zypHhXaHsre2l1Igdj0mgs25MPpvE7yBZNvyan8Vx0h
843 36Hj8r4Gh3og3YNfvem67sNTwNwONY0ep+Xho/3vG0jFATGduSXdcT04DusgZNqg
844 UJqW75cqxrD6o/nya5wUoN9NL5pcd5AgVMdOYvJGbrwQuaECQQDiCs/5dsUkUkeC
845 Tlur1wh0wJpW4Y2ctO3ncRdnAoAA9y8dELHXMqwKE4HtlyzHY7Bxds/BDh373EVK
846 rsdl+v9JAkEAx3xTmsOQvWa1tf/O30sdItVpGogKDvYqkLCNthUzPaL85BWB03E2
847 xunHcVVlqAOE5tFuw0/UEyEkOaGlNTJTzwJAPIVel9FoCUiKYuYt/z1swy3KZRaw
848 /tMmm4AZHvh5Y0jLcYHFy/OCQpRkhkOitqQHWunPyEXKW2PnnY5cTv68GQJAHG7H
849 B88KCUTjb25nkQIGxBlA4swzCtDhXkAb4rEA3a8mdmfuWjHPyeg2ShwO4jSmM7P0
850 Iph1NMjLff9hKcTjlwJARpItOFkYEdtSODC7FMm7KRKQnNB27gFAizsOYWD4D2b7
851 w1FTEZ/kSA9wSNhyNGt7dgUo6zFhm2u973HBCUb3dg==
852 -----END RSA PRIVATE KEY-----
853 """, # 5
854 """-----BEGIN CERTIFICATE-----
855 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
856 aW5neTAeFw0wODA3MjUyMjQ3NThaFw0wOTA3MjUyMjQ3NThaMBcxFTATBgNVBAMU
857 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvhTRj1dA
858 NOfse/UBeTfMekZKxZHsNPr+qBYaveWAHDded/BMyMgaMV2n6HQdiDaRjJkzjHCF
859 3xBtpIJeEGUqfrF0ob8BIZXy3qk68eX/0CVUbgmjSBN44ahlo63NshyXmZtEAkRV
860 VE/+cRKw3N2wtuTed5xwfNcL6dg4KTOEYEkCAwEAATANBgkqhkiG9w0BAQQFAAOB
861 gQCN+CLuVwLeWjSdVbdizYyrOVckqtwiIHG9BbGMlcIdm0qpvD7V7/sN2csk5LaT
862 BNiHi1t5628/4UHqqodYmFw8ri8ItFwB+MmTJi11CX6dIP9OUhS0qO8Z/BKtot7H
863 j04oNwl+WqZZfHIYwTIEL0HBn60nOvCQPDtnWG2BhpUxMA==
864 -----END CERTIFICATE-----
865 -----BEGIN RSA PRIVATE KEY-----
866 MIICXQIBAAKBgQC+FNGPV0A05+x79QF5N8x6RkrFkew0+v6oFhq95YAcN1538EzI
867 yBoxXafodB2INpGMmTOMcIXfEG2kgl4QZSp+sXShvwEhlfLeqTrx5f/QJVRuCaNI
868 E3jhqGWjrc2yHJeZm0QCRFVUT/5xErDc3bC25N53nHB81wvp2DgpM4RgSQIDAQAB
869 AoGALl2BqIdN4Bnac3oV++2CcSkIQB0SEvJOf820hDGhCEDxSCxTbn5w9S21MVxx
870 f7Jf2n3cNxuTbA/jzscGDtW+gXCs+WAbAr5aOqHLUPGEobhKQrQT2hrxQHyv3UFp
871 0tIl9eXFknOyVAaUJ3athK5tyjSiCZQQHLGzeLaDSKVAPqECQQD1GK7DkTcLaSvw
872 hoTJ3dBK3JoKT2HHLitfEE0QV58mkqFMjofpe+nyeKWvEb/oB4WBp/cfTvtf7DJK
873 zl1OSf11AkEAxomWmJeub0xpqksCmnVI1Jt1mvmcE4xpIcXq8sxzLHRc2QOv0kTw
874 IcFl4QcN6EQBmE+8kl7Tx8SPAVKfJMoZBQJAGsUFYYrczjxAdlba7glyFJsfn/yn
875 m0+poQpwwFYxpc7iGzB+G7xTAw62WfbAVSFtLYog7aR8xC9SFuWPP1vJeQJBAILo
876 xBj3ovgWTXIRJbVM8mnl28UFI0msgsHXK9VOw/6i93nMuYkPFbtcN14KdbwZ42dX
877 5EIrLr+BNr4riW4LqDUCQQCbsEEpTmj3upKUOONPt+6CH/OOMjazUzYHZ/3ORHGp
878 Q3Wt+I4IrR/OsiACSIQAhS4kBfk/LGggnj56DrWt+oBl
879 -----END RSA PRIVATE KEY-----
880 """, #6
881 """-----BEGIN CERTIFICATE-----
882 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
883 aW5neTAeFw0wODA3MjUyMjQ3NThaFw0wOTA3MjUyMjQ3NThaMBcxFTATBgNVBAMU
884 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtKhx6sEA
885 jn6HWc6T2klwlPn0quyHtATIw8V3ezP46v6g2rRS7dTywo4GTP4vX58l+sC9z9Je
886 qhQ1rWSwMK4FmnDMZCu7AVO7oMIXpXdSz7l0bgCnNjvbpkA2pOfbB1Z8oj8iebff
887 J33ID5DdkmCzqYVtKpII1o/5z7Jo292JYy8CAwEAATANBgkqhkiG9w0BAQQFAAOB
888 gQA0PYMA07wo9kEH4fv9TCfo+zz42Px6lUxrQBPxBvDiGYhk2kME/wX0IcoZPKTV
889 WyBGmDAYWvFaHWbrbbTOfzlLWfYrDD913hCi9cO8iF8oBqRjIlkKcxAoe7vVg5Az
890 ydVcrY+zqULJovWwyNmH1QNIQfMat0rj7fylwjiS1y/YsA==
891 -----END CERTIFICATE-----
892 -----BEGIN RSA PRIVATE KEY-----
893 MIICXAIBAAKBgQC0qHHqwQCOfodZzpPaSXCU+fSq7Ie0BMjDxXd7M/jq/qDatFLt
894 1PLCjgZM/i9fnyX6wL3P0l6qFDWtZLAwrgWacMxkK7sBU7ugwheld1LPuXRuAKc2
895 O9umQDak59sHVnyiPyJ5t98nfcgPkN2SYLOphW0qkgjWj/nPsmjb3YljLwIDAQAB
896 AoGAU4CYRv22mCZ7wVLunDLdyr5ODMMPZnHfqj2XoGbBYz0WdIBs5GlNXAfxeZzz
897 oKsbDvAPzANcphh5RxAHMDj/dT8rZOez+eJrs1GEV+crl1T9p83iUkAuOJFtgUgf
898 TtQBL9vHaj7DfvCEXcBPmN/teDFmAAOyUNbtuhTkRa3PbuECQQDwaqZ45Kr0natH
899 V312dqlf9ms8I6e873pAu+RvA3BAWczk65eGcRjEBxVpTvNEcYKFrV8O5ZYtolrr
900 VJl97AfdAkEAwF4w4KJ32fLPVoPnrYlgLw86NejMpAkixblm8cn51avPQmwbtahb
901 BZUuca22IpgDpjeEk5SpEMixKe/UjzxMewJBALy4q2cY8U3F+u6sshLtAPYQZIs3
902 3fNE9W2dUKsIQvRwyZMlkLN7UhqHCPq6e+HNTM0MlCMIfAPkf4Rdy4N6ZY0CQCKE
903 BAMaQ6TwgzFDw5sIjiCDe+9WUPmRxhJyHL1/fvtOs4Z4fVRP290ZklbFU2vLmMQH
904 LBuKzfb7+4XJyXrV1+cCQBqfPFQQZLr5UgccABYQ2jnWVbJPISJ5h2b0cwXt+pz/
905 8ODEYLjqWr9K8dtbgwdpzwbkaGhQYpyvsguMvNPMohs=
906 -----END RSA PRIVATE KEY-----
907 """, #7
908 """-----BEGIN CERTIFICATE-----
909 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
910 aW5neTAeFw0wODA3MjUyMjQ3NThaFw0wOTA3MjUyMjQ3NThaMBcxFTATBgNVBAMU
911 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAnBfNHycn
912 5RnYzDN4EWTk2q1BBxA6ZYtlG1WPkj5iKeaYKzUk58zBL7mNOA0ucq+yTwh9C4IC
913 EutWPaKBSKY5XI+Rdebh+Efq+urtOLgfJHlfcCraEx7hYN+tqqMVgEgnO/MqIsn1
914 I1Fvnp89mSYbQ9tmvhSH4Hm+nbeK6iL2tIsCAwEAATANBgkqhkiG9w0BAQQFAAOB
915 gQBt9zxfsKWoyyV764rRb6XThuTDMNSDaVofqePEWjudAbDu6tp0pHcrL0XpIrnT
916 3iPgD47pdlwQNbGJ7xXwZu2QTOq+Lv62E6PCL8FljDVoYqR3WwJFFUigNvBT2Zzu
917 Pxx7KUfOlm/M4XUSMu31sNJ0kQniBwpkW43YmHVNFb/R7g==
918 -----END CERTIFICATE-----
919 -----BEGIN RSA PRIVATE KEY-----
920 MIICXQIBAAKBgQCcF80fJyflGdjMM3gRZOTarUEHEDpli2UbVY+SPmIp5pgrNSTn
921 zMEvuY04DS5yr7JPCH0LggIS61Y9ooFIpjlcj5F15uH4R+r66u04uB8keV9wKtoT
922 HuFg362qoxWASCc78yoiyfUjUW+enz2ZJhtD22a+FIfgeb6dt4rqIva0iwIDAQAB
923 AoGBAIHstcnWd7iUeQYPWUNxLaRvTY8pjNH04yWLZEOgNWkXDVX5mExw++RTmB4t
924 qpm/cLWkJSEtB7jjthb7ao0j/t2ljqfr6kAbClDv3zByAEDhOu8xB/5ne6Ioo+k2
925 dygC+GcVcobhv8qRU+z0fpeXSP8yS1bQQHOaa17bSGsncvHRAkEAzwsn8jBTOqaW
926 6Iymvr7Aql++LiwEBrqMMRVyBZlkux4hiKa2P7XXEL6/mOPR0aI2LuCqE2COrO7R
927 0wAFZ54bjwJBAMEAe6cs0zI3p3STHwA3LoSZB81lzLhGUnYBvOq1yoDSlJCOYpld
928 YM1y3eC0vwiOnEu3GG1bhkW+h6Kx0I/qyUUCQBiH9NqwORxI4rZ4+8S76y4EnA7y
929 biOx9KxYIyNgslutTUHYpt1TmUDFqQPfclvJQWw6eExFc4Iv5bJ/XSSSyicCQGyY
930 5PrwEfYTsrm5fpwUcKxTnzxHp6WYjBWybKZ0m/lYhBfCxmAdVrbDh21Exqj99Zv0
931 7l26PhdIWfGFtCEGrzECQQCtPyXa3ostSceR7zEKxyn9QBCNXKARfNNTBja6+VRE
932 qDC6jLqzu/SoOYaqa13QzCsttO2iZk8Ygfy3Yz0n37GE
933 -----END RSA PRIVATE KEY-----
934 """, #8
935 """-----BEGIN CERTIFICATE-----
936 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
937 aW5neTAeFw0wODA3MjUyMjQ3NThaFw0wOTA3MjUyMjQ3NThaMBcxFTATBgNVBAMU
938 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4mnLf+x0
939 CWKDKP5PLZ87t2ReSDE/J5QoI5VhE0bXaahdhPrQTC2wvOpT+N9nzEpI9ASh/ejV
940 kYGlc03nNKRL7zyVM1UyGduEwsRssFMqfyJhI1p+VmxDMWNplex7mIAheAdskPj3
941 pwi2CP4VIMjOj368AXvXItPzeCfAhYhEVaMCAwEAATANBgkqhkiG9w0BAQQFAAOB
942 gQAEzmwq5JFI5Z0dX20m9rq7NKgwRyAH3h5aE8bdjO8nEc69qscfDRx79Lws3kK8
943 A0LG0DhxKB8cTNu3u+jy81tjcC4pLNQ5IKap9ksmP7RtIHfTA55G8M3fPl2ZgDYQ
944 ZzsWAZvTNXd/eme0SgOzD10rfntA6ZIgJTWHx3E0RkdwKw==
945 -----END CERTIFICATE-----
946 -----BEGIN RSA PRIVATE KEY-----
947 MIICXQIBAAKBgQDiact/7HQJYoMo/k8tnzu3ZF5IMT8nlCgjlWETRtdpqF2E+tBM
948 LbC86lP432fMSkj0BKH96NWRgaVzTec0pEvvPJUzVTIZ24TCxGywUyp/ImEjWn5W
949 bEMxY2mV7HuYgCF4B2yQ+PenCLYI/hUgyM6PfrwBe9ci0/N4J8CFiERVowIDAQAB
950 AoGAQYTl+8XcKl8Un4dAOG6M5FwqIHAH25c3Klzu85obehrbvUCriG/sZi7VT/6u
951 VeLlS6APlJ+NNgczbrOLhaNJyYzjICSt8BI96PldFUzCEkVlgE+29pO7RNoZmDYB
952 dSGyIDrWdVYfdzpir6kC0KDcrpA16Sc+/bK6Q8ALLRpC7QECQQD7F7fhIQ03CKSk
953 lS4mgDuBQrB/52jXgBumtjp71ANNeaWR6+06KDPTLysM+olsh97Q7YOGORbrBnBg
954 Y2HPnOgjAkEA5taZaMfdFa8V1SPcX7mgCLykYIujqss0AmauZN/24oLdNE8HtTBF
955 OLaxE6PnQ0JWfx9KGIy3E0V3aFk5FWb0gQJBAO4KFEaXgOG1jfCBhNj3JHJseMso
956 5Nm4F366r0MJQYBHXNGzqphB2K/Svat2MKX1QSUspk2u/a0d05dtYCLki6UCQHWS
957 sChyQ+UbfF9HGKOZBC3vBzo1ZXNEdIUUj5bJjBHq3YgbCK38nAU66A482TmkvDGb
958 Wj4OzeB+7Ua0yyJfggECQQDVlAa8HqdAcrbEwI/YfPydFsavBJ0KtcIGK2owQ+dk
959 dhlDnpXDud/AtX4Ft2LaquQ15fteRrYjjwI9SFGytjtp
960 -----END RSA PRIVATE KEY-----
961 """, #9
962 """-----BEGIN CERTIFICATE-----
963 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
964 aW5neTAeFw0wODA3MjUyMjQ3NThaFw0wOTA3MjUyMjQ3NThaMBcxFTATBgNVBAMU
965 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAueLfowPT
966 kXXtHeU2FZSz2mJhHmjqeyI1oMoyyggonccx65vMxaRfljnz2dOjVVYpCOn/LrdP
967 wVxHO8KNDsmQeWPRjnnBa2dFqqOnp/8gEJFJBW7K/gI9se6o+xe9QIWBq6d/fKVR
968 BURJe5TycLogzZuxQn1xHHILa3XleYuHAbMCAwEAATANBgkqhkiG9w0BAQQFAAOB
969 gQBEC1lfC3XK0galQC96B7faLpnQmhn5lX2FUUoFIQQtBTetoE+gTqnLSOIZcOK4
970 pkT3YvxUvgOV0LOLClryo2IknMMGWRSAcXtVUBBLRHVTSSuVUyyLr5kdRU7B4E+l
971 OU0j8Md/dzlkm//K1bzLyUaPq204ofH8su2IEX4b3IGmAQ==
972 -----END CERTIFICATE-----
973 -----BEGIN RSA PRIVATE KEY-----
974 MIICWwIBAAKBgQC54t+jA9ORde0d5TYVlLPaYmEeaOp7IjWgyjLKCCidxzHrm8zF
975 pF+WOfPZ06NVVikI6f8ut0/BXEc7wo0OyZB5Y9GOecFrZ0Wqo6en/yAQkUkFbsr+
976 Aj2x7qj7F71AhYGrp398pVEFREl7lPJwuiDNm7FCfXEccgtrdeV5i4cBswIDAQAB
977 AoGAO4PnJHNaLs16AMNdgKVevEIZZDolMQ1v7C4w+ryH/JRFaHE2q+UH8bpWV9zK
978 A82VT9RTrqpkb71S1VBiB2UDyz263XdAI/N2HcIVMmfKb72oV4gCI1KOv4DfFwZv
979 tVVcIdVEDBOZ2TgqK4opGOgWMDqgIAl2z3PbsIoNylZHEJECQQDtQeJFhEJGH4Qz
980 BGpdND0j2nnnJyhOFHJqikJNdul3uBwmxTK8FPEUUH/rtpyUan3VMOyDx3kX4OQg
981 GDNSb32rAkEAyJIZIJ0EMRHVedyWsfqR0zTGKRQ+qsc3sCfyUhFksWms9jsSS0DT
982 tVeTdC3F6EIAdpKOGhSyfBTU4jxwbFc0GQJADI4L9znEeAl66Wg2aLA2/Aq3oK/F
983 xjv2wgSG9apxOFCZzMNqp+FD0Jth6YtEReZMuldYbLDFi6nu6HPfY2Fa+QJAdpm1
984 lAxk6yMxiZK/5VRWoH6HYske2Vtd+aNVbePtF992ME/z3F3kEkpL3hom+dT1cyfs
985 MU3l0Ot8ip7Ul6vlGQJAegNzpcfl2GFSdWQMxQ+nN3woKnPqpR1M3jgnqvo7L4Xe
986 JW3vRxvfdrUuzdlvZ/Pbsu/vOd+cuIa4h0yD5q3N+g==
987 -----END RSA PRIVATE KEY-----
988 """, #10
989 """-----BEGIN CERTIFICATE-----
990 MIIBnjCCAQcCAgCEMA0GCSqGSIb3DQEBBAUAMBcxFTATBgNVBAMUDG5ld3BiX3Ro
991 aW5neTAeFw0wODA3MjUyMjQ3NThaFw0wOTA3MjUyMjQ3NThaMBcxFTATBgNVBAMU
992 DG5ld3BiX3RoaW5neTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAruBhwk+J
993 XdlwfKXXN8K+43JyEYCV7Fp7ZiES4t4AEJuQuBqJVMxpzeZzu2t/vVb59ThaxxtY
994 NGD3Xy6Og5dTv//ztWng8P7HwwvfbrUICU6zo6JAhg7kfaNa116krCYOkC/cdJWt
995 o5W+zsDmI1jUVGH0D73h29atc1gn6wLpAsMCAwEAATANBgkqhkiG9w0BAQQFAAOB
996 gQAEJ/ITGJ9lK/rk0yHcenW8SHsaSTlZMuJ4yEiIgrJ2t71Rd6mtCC/ljx9USvvK
997 bF500whTiZlnWgKi02boBEKa44z/DytF6pljeNPefBQSqZyUByGEb/8Mn58Idyls
998 q4/d9iKXMPvbpQdcesOzgOffFZevLQSWyPRaIdYBOOiYUA==
999 -----END CERTIFICATE-----
1000 -----BEGIN RSA PRIVATE KEY-----
1001 MIICXQIBAAKBgQCu4GHCT4ld2XB8pdc3wr7jcnIRgJXsWntmIRLi3gAQm5C4GolU
1002 zGnN5nO7a3+9Vvn1OFrHG1g0YPdfLo6Dl1O///O1aeDw/sfDC99utQgJTrOjokCG
1003 DuR9o1rXXqSsJg6QL9x0la2jlb7OwOYjWNRUYfQPveHb1q1zWCfrAukCwwIDAQAB
1004 AoGAcZAXC/dYrlBpIxkTRQu7qLqGZuVI9t7fabgqqpceFargdR4Odrn0L5jrKRer
1005 MYrM8bjyAoC4a/NYUUBLnhrkcCQWO9q5fSQuFKFVWHY53SM63Qdqk8Y9Fmy/h/4c
1006 UtwZ5BWkUWItvnTMgb9bFcvSiIhEcNQauypnMpgNknopu7kCQQDlSQT10LkX2IGT
1007 bTUhPcManx92gucaKsPONKq2mP+1sIciThevRTZWZsxyIuoBBY43NcKKi8NlZCtj
1008 hhSbtzYdAkEAw0B93CXfso8g2QIMj/HJJz/wNTLtg+rriXp6jh5HWe6lKWRVrce+
1009 1w8Qz6OI/ZP6xuQ9HNeZxJ/W6rZPW6BGXwJAHcTuRPA1p/fvUvHh7Q/0zfcNAbkb
1010 QlV9GL/TzmNtB+0EjpqvDo2g8XTlZIhN85YCEf8D5DMjSn3H+GMHN/SArQJBAJlW
1011 MIGPjNoh5V4Hae4xqBOW9wIQeM880rUo5s5toQNTk4mqLk9Hquwh/MXUXGUora08
1012 2XGpMC1midXSTwhaGmkCQQCdivptFEYl33PrVbxY9nzHynpp4Mi89vQF0cjCmaYY
1013 N8L+bvLd4BU9g6hRS8b59lQ6GNjryx2bUnCVtLcey4Jd
1014 -----END RSA PRIVATE KEY-----
1015 """, #11
1016 ]
1017
1018 # To disable the pre-computed tub certs, uncomment this line.
1019 #SYSTEM_TEST_CERTS = []
1020
1021 TEST_DATA="\x02"*(immutable.upload.Uploader.URI_LIT_SIZE_THRESHOLD+1)
1022
1023 class ShouldFailMixin:
1024     def shouldFail(self, expected_failure, which, substring,
1025                    callable, *args, **kwargs):
1026         """Assert that a function call raises some exception. This is a
1027         Deferred-friendly version of TestCase.assertRaises() .
1028
1029         Suppose you want to verify the following function:
1030
1031          def broken(a, b, c):
1032              if a < 0:
1033                  raise TypeError('a must not be negative')
1034              return defer.succeed(b+c)
1035
1036         You can use:
1037             d = self.shouldFail(TypeError, 'test name',
1038                                 'a must not be negative',
1039                                 broken, -4, 5, c=12)
1040         in your test method. The 'test name' string will be included in the
1041         error message, if any, because Deferred chains frequently make it
1042         difficult to tell which assertion was tripped.
1043
1044         The substring= argument, if not None, must appear in the 'repr'
1045         of the message wrapped by this Failure, or the test will fail.
1046         """
1047
1048         assert substring is None or isinstance(substring, str)
1049         d = defer.maybeDeferred(callable, *args, **kwargs)
1050         def done(res):
1051             if isinstance(res, failure.Failure):
1052                 res.trap(expected_failure)
1053                 if substring:
1054                     message = repr(res.value.args[0])
1055                     self.failUnless(substring in message,
1056                                     "%s: substring '%s' not in '%s'"
1057                                     % (which, substring, message))
1058             else:
1059                 self.fail("%s was supposed to raise %s, not get '%s'" %
1060                           (which, expected_failure, res))
1061         d.addBoth(done)
1062         return d
1063
1064 class WebErrorMixin:
1065     def explain_web_error(self, f):
1066         # an error on the server side causes the client-side getPage() to
1067         # return a failure(t.web.error.Error), and its str() doesn't show the
1068         # response body, which is where the useful information lives. Attach
1069         # this method as an errback handler, and it will reveal the hidden
1070         # message.
1071         f.trap(WebError)
1072         print "Web Error:", f.value, ":", f.value.response
1073         return f
1074
1075     def _shouldHTTPError(self, res, which, validator):
1076         if isinstance(res, failure.Failure):
1077             res.trap(WebError)
1078             return validator(res)
1079         else:
1080             self.fail("%s was supposed to Error, not get '%s'" % (which, res))
1081
1082     def shouldHTTPError(self, which,
1083                         code=None, substring=None, response_substring=None,
1084                         callable=None, *args, **kwargs):
1085         # returns a Deferred with the response body
1086         assert substring is None or isinstance(substring, str)
1087         assert callable
1088         def _validate(f):
1089             if code is not None:
1090                 self.failUnlessEqual(f.value.status, str(code), which)
1091             if substring:
1092                 code_string = str(f)
1093                 self.failUnless(substring in code_string,
1094                                 "%s: substring '%s' not in '%s'"
1095                                 % (which, substring, code_string))
1096             response_body = f.value.response
1097             if response_substring:
1098                 self.failUnless(response_substring in response_body,
1099                                 "%s: response substring '%s' not in '%s'"
1100                                 % (which, response_substring, response_body))
1101             return response_body
1102         d = defer.maybeDeferred(callable, *args, **kwargs)
1103         d.addBoth(self._shouldHTTPError, which, _validate)
1104         return d
1105
1106 class ErrorMixin(WebErrorMixin):
1107     def explain_error(self, f):
1108         if f.check(defer.FirstError):
1109             print "First Error:", f.value.subFailure
1110         return f
1111
1112 def corrupt_field(data, offset, size, debug=False):
1113     if random.random() < 0.5:
1114         newdata = testutil.flip_one_bit(data, offset, size)
1115         if debug:
1116             log.msg("testing: corrupting offset %d, size %d flipping one bit orig: %r, newdata: %r" % (offset, size, data[offset:offset+size], newdata[offset:offset+size]))
1117         return newdata
1118     else:
1119         newval = testutil.insecurerandstr(size)
1120         if debug:
1121             log.msg("testing: corrupting offset %d, size %d randomizing field, orig: %r, newval: %r" % (offset, size, data[offset:offset+size], newval))
1122         return data[:offset]+newval+data[offset+size:]
1123
1124 def _corrupt_nothing(data, debug=False):
1125     """Leave the data pristine. """
1126     return data
1127
1128 def _corrupt_file_version_number(data, debug=False):
1129     """Scramble the file data -- the share file version number have one bit
1130     flipped or else will be changed to a random value."""
1131     return corrupt_field(data, 0x00, 4)
1132
1133 def _corrupt_size_of_file_data(data, debug=False):
1134     """Scramble the file data -- the field showing the size of the share data
1135     within the file will be set to one smaller."""
1136     return corrupt_field(data, 0x04, 4)
1137
1138 def _corrupt_sharedata_version_number(data, debug=False):
1139     """Scramble the file data -- the share data version number will have one
1140     bit flipped or else will be changed to a random value, but not 1 or 2."""
1141     return corrupt_field(data, 0x0c, 4)
1142     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1143     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1144     newsharevernum = sharevernum
1145     while newsharevernum in (1, 2):
1146         newsharevernum = random.randrange(0, 2**32)
1147     newsharevernumbytes = struct.pack(">L", newsharevernum)
1148     return data[:0x0c] + newsharevernumbytes + data[0x0c+4:]
1149
1150 def _corrupt_sharedata_version_number_to_plausible_version(data, debug=False):
1151     """Scramble the file data -- the share data version number will be
1152     changed to 2 if it is 1 or else to 1 if it is 2."""
1153     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1154     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1155     if sharevernum == 1:
1156         newsharevernum = 2
1157     else:
1158         newsharevernum = 1
1159     newsharevernumbytes = struct.pack(">L", newsharevernum)
1160     return data[:0x0c] + newsharevernumbytes + data[0x0c+4:]
1161
1162 def _corrupt_segment_size(data, debug=False):
1163     """Scramble the file data -- the field showing the size of the segment
1164     will have one bit flipped or else be changed to a random value."""
1165     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1166     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1167     if sharevernum == 1:
1168         return corrupt_field(data, 0x0c+0x04, 4, debug=False)
1169     else:
1170         return corrupt_field(data, 0x0c+0x04, 8, debug=False)
1171
1172 def _corrupt_size_of_sharedata(data, debug=False):
1173     """Scramble the file data -- the field showing the size of the data
1174     within the share data will have one bit flipped or else will be changed
1175     to a random value."""
1176     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1177     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1178     if sharevernum == 1:
1179         return corrupt_field(data, 0x0c+0x08, 4)
1180     else:
1181         return corrupt_field(data, 0x0c+0x0c, 8)
1182
1183 def _corrupt_offset_of_sharedata(data, debug=False):
1184     """Scramble the file data -- the field showing the offset of the data
1185     within the share data will have one bit flipped or else be changed to a
1186     random value."""
1187     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1188     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1189     if sharevernum == 1:
1190         return corrupt_field(data, 0x0c+0x0c, 4)
1191     else:
1192         return corrupt_field(data, 0x0c+0x14, 8)
1193
1194 def _corrupt_offset_of_ciphertext_hash_tree(data, debug=False):
1195     """Scramble the file data -- the field showing the offset of the
1196     ciphertext hash tree within the share data will have one bit flipped or
1197     else be changed to a random value.
1198     """
1199     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1200     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1201     if sharevernum == 1:
1202         return corrupt_field(data, 0x0c+0x14, 4, debug=False)
1203     else:
1204         return corrupt_field(data, 0x0c+0x24, 8, debug=False)
1205
1206 def _corrupt_offset_of_block_hashes(data, debug=False):
1207     """Scramble the file data -- the field showing the offset of the block
1208     hash tree within the share data will have one bit flipped or else will be
1209     changed to a random value."""
1210     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1211     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1212     if sharevernum == 1:
1213         return corrupt_field(data, 0x0c+0x18, 4)
1214     else:
1215         return corrupt_field(data, 0x0c+0x2c, 8)
1216
1217 def _corrupt_offset_of_block_hashes_to_truncate_crypttext_hashes(data, debug=False):
1218     """Scramble the file data -- the field showing the offset of the block
1219     hash tree within the share data will have a multiple of hash size
1220     subtracted from it, thus causing the downloader to download an incomplete
1221     crypttext hash tree."""
1222     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1223     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1224     if sharevernum == 1:
1225         curval = struct.unpack(">L", data[0x0c+0x18:0x0c+0x18+4])[0]
1226         newval = random.randrange(0, max(1, (curval/hashutil.CRYPTO_VAL_SIZE)/2))*hashutil.CRYPTO_VAL_SIZE
1227         newvalstr = struct.pack(">L", newval)
1228         return data[:0x0c+0x18]+newvalstr+data[0x0c+0x18+4:]
1229     else:
1230         curval = struct.unpack(">Q", data[0x0c+0x2c:0x0c+0x2c+8])[0]
1231         newval = random.randrange(0, max(1, (curval/hashutil.CRYPTO_VAL_SIZE)/2))*hashutil.CRYPTO_VAL_SIZE
1232         newvalstr = struct.pack(">Q", newval)
1233         return data[:0x0c+0x2c]+newvalstr+data[0x0c+0x2c+8:]
1234
1235 def _corrupt_offset_of_share_hashes(data, debug=False):
1236     """Scramble the file data -- the field showing the offset of the share
1237     hash tree within the share data will have one bit flipped or else will be
1238     changed to a random value."""
1239     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1240     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1241     if sharevernum == 1:
1242         return corrupt_field(data, 0x0c+0x1c, 4)
1243     else:
1244         return corrupt_field(data, 0x0c+0x34, 8)
1245
1246 def _corrupt_offset_of_uri_extension(data, debug=False):
1247     """Scramble the file data -- the field showing the offset of the uri
1248     extension will have one bit flipped or else will be changed to a random
1249     value."""
1250     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1251     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1252     if sharevernum == 1:
1253         return corrupt_field(data, 0x0c+0x20, 4)
1254     else:
1255         return corrupt_field(data, 0x0c+0x3c, 8)
1256
1257 def _corrupt_offset_of_uri_extension_to_force_short_read(data, debug=False):
1258     """Scramble the file data -- the field showing the offset of the uri
1259     extension will be set to the size of the file minus 3. This means when
1260     the client tries to read the length field from that location it will get
1261     a short read -- the result string will be only 3 bytes long, not the 4 or
1262     8 bytes necessary to do a successful struct.unpack."""
1263     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1264     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1265     # The "-0x0c" in here is to skip the server-side header in the share
1266     # file, which the client doesn't see when seeking and reading.
1267     if sharevernum == 1:
1268         if debug:
1269             log.msg("testing: corrupting offset %d, size %d, changing %d to %d (len(data) == %d)" % (0x2c, 4, struct.unpack(">L", data[0x2c:0x2c+4])[0], len(data)-0x0c-3, len(data)))
1270         return data[:0x2c] + struct.pack(">L", len(data)-0x0c-3) + data[0x2c+4:]
1271     else:
1272         if debug:
1273             log.msg("testing: corrupting offset %d, size %d, changing %d to %d (len(data) == %d)" % (0x48, 8, struct.unpack(">Q", data[0x48:0x48+8])[0], len(data)-0x0c-3, len(data)))
1274         return data[:0x48] + struct.pack(">Q", len(data)-0x0c-3) + data[0x48+8:]
1275
1276 def _corrupt_mutable_share_data(data, debug=False):
1277     prefix = data[:32]
1278     assert prefix == MutableShareFile.MAGIC, "This function is designed to corrupt mutable shares of v1, and the magic number doesn't look right: %r vs %r" % (prefix, MutableShareFile.MAGIC)
1279     data_offset = MutableShareFile.DATA_OFFSET
1280     sharetype = data[data_offset:data_offset+1]
1281     assert sharetype == "\x00", "non-SDMF mutable shares not supported"
1282     (version, ig_seqnum, ig_roothash, ig_IV, ig_k, ig_N, ig_segsize,
1283      ig_datalen, offsets) = unpack_header(data[data_offset:])
1284     assert version == 0, "this function only handles v0 SDMF files"
1285     start = data_offset + offsets["share_data"]
1286     length = data_offset + offsets["enc_privkey"] - start
1287     return corrupt_field(data, start, length)
1288
1289 def _corrupt_share_data(data, debug=False):
1290     """Scramble the file data -- the field containing the share data itself
1291     will have one bit flipped or else will be changed to a random value."""
1292     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1293     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways, not v%d." % sharevernum
1294     if sharevernum == 1:
1295         sharedatasize = struct.unpack(">L", data[0x0c+0x08:0x0c+0x08+4])[0]
1296
1297         return corrupt_field(data, 0x0c+0x24, sharedatasize)
1298     else:
1299         sharedatasize = struct.unpack(">Q", data[0x0c+0x08:0x0c+0x0c+8])[0]
1300
1301         return corrupt_field(data, 0x0c+0x44, sharedatasize)
1302
1303 def _corrupt_share_data_last_byte(data, debug=False):
1304     """Scramble the file data -- flip all bits of the last byte."""
1305     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1306     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways, not v%d." % sharevernum
1307     if sharevernum == 1:
1308         sharedatasize = struct.unpack(">L", data[0x0c+0x08:0x0c+0x08+4])[0]
1309         offset = 0x0c+0x24+sharedatasize-1
1310     else:
1311         sharedatasize = struct.unpack(">Q", data[0x0c+0x08:0x0c+0x0c+8])[0]
1312         offset = 0x0c+0x44+sharedatasize-1
1313
1314     newdata = data[:offset] + chr(ord(data[offset])^0xFF) + data[offset+1:]
1315     if debug:
1316         log.msg("testing: flipping all bits of byte at offset %d: %r, newdata: %r" % (offset, data[offset], newdata[offset]))
1317     return newdata
1318
1319 def _corrupt_crypttext_hash_tree(data, debug=False):
1320     """Scramble the file data -- the field containing the crypttext hash tree
1321     will have one bit flipped or else will be changed to a random value.
1322     """
1323     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1324     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1325     if sharevernum == 1:
1326         crypttexthashtreeoffset = struct.unpack(">L", data[0x0c+0x14:0x0c+0x14+4])[0]
1327         blockhashesoffset = struct.unpack(">L", data[0x0c+0x18:0x0c+0x18+4])[0]
1328     else:
1329         crypttexthashtreeoffset = struct.unpack(">Q", data[0x0c+0x24:0x0c+0x24+8])[0]
1330         blockhashesoffset = struct.unpack(">Q", data[0x0c+0x2c:0x0c+0x2c+8])[0]
1331
1332     return corrupt_field(data, 0x0c+crypttexthashtreeoffset, blockhashesoffset-crypttexthashtreeoffset, debug=debug)
1333
1334 def _corrupt_crypttext_hash_tree_byte_x221(data, debug=False):
1335     """Scramble the file data -- the byte at offset 0x221 will have its 7th
1336     (b1) bit flipped.
1337     """
1338     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1339     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1340     if debug:
1341         log.msg("original data: %r" % (data,))
1342     return data[:0x0c+0x221] + chr(ord(data[0x0c+0x221])^0x02) + data[0x0c+0x2210+1:]
1343
1344 def _corrupt_block_hashes(data, debug=False):
1345     """Scramble the file data -- the field containing the block hash tree
1346     will have one bit flipped or else will be changed to a random value.
1347     """
1348     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1349     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1350     if sharevernum == 1:
1351         blockhashesoffset = struct.unpack(">L", data[0x0c+0x18:0x0c+0x18+4])[0]
1352         sharehashesoffset = struct.unpack(">L", data[0x0c+0x1c:0x0c+0x1c+4])[0]
1353     else:
1354         blockhashesoffset = struct.unpack(">Q", data[0x0c+0x2c:0x0c+0x2c+8])[0]
1355         sharehashesoffset = struct.unpack(">Q", data[0x0c+0x34:0x0c+0x34+8])[0]
1356
1357     return corrupt_field(data, 0x0c+blockhashesoffset, sharehashesoffset-blockhashesoffset)
1358
1359 def _corrupt_share_hashes(data, debug=False):
1360     """Scramble the file data -- the field containing the share hash chain
1361     will have one bit flipped or else will be changed to a random value.
1362     """
1363     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1364     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1365     if sharevernum == 1:
1366         sharehashesoffset = struct.unpack(">L", data[0x0c+0x1c:0x0c+0x1c+4])[0]
1367         uriextoffset = struct.unpack(">L", data[0x0c+0x20:0x0c+0x20+4])[0]
1368     else:
1369         sharehashesoffset = struct.unpack(">Q", data[0x0c+0x34:0x0c+0x34+8])[0]
1370         uriextoffset = struct.unpack(">Q", data[0x0c+0x3c:0x0c+0x3c+8])[0]
1371
1372     return corrupt_field(data, 0x0c+sharehashesoffset, uriextoffset-sharehashesoffset)
1373
1374 def _corrupt_length_of_uri_extension(data, debug=False):
1375     """Scramble the file data -- the field showing the length of the uri
1376     extension will have one bit flipped or else will be changed to a random
1377     value."""
1378     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1379     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1380     if sharevernum == 1:
1381         uriextoffset = struct.unpack(">L", data[0x0c+0x20:0x0c+0x20+4])[0]
1382         return corrupt_field(data, uriextoffset, 4)
1383     else:
1384         uriextoffset = struct.unpack(">Q", data[0x0c+0x3c:0x0c+0x3c+8])[0]
1385         return corrupt_field(data, 0x0c+uriextoffset, 8)
1386
1387 def _corrupt_uri_extension(data, debug=False):
1388     """Scramble the file data -- the field containing the uri extension will
1389     have one bit flipped or else will be changed to a random value."""
1390     sharevernum = struct.unpack(">L", data[0x0c:0x0c+4])[0]
1391     assert sharevernum in (1, 2), "This test is designed to corrupt immutable shares of v1 or v2 in specific ways."
1392     if sharevernum == 1:
1393         uriextoffset = struct.unpack(">L", data[0x0c+0x20:0x0c+0x20+4])[0]
1394         uriextlen = struct.unpack(">L", data[0x0c+uriextoffset:0x0c+uriextoffset+4])[0]
1395     else:
1396         uriextoffset = struct.unpack(">Q", data[0x0c+0x3c:0x0c+0x3c+8])[0]
1397         uriextlen = struct.unpack(">Q", data[0x0c+uriextoffset:0x0c+uriextoffset+8])[0]
1398
1399     return corrupt_field(data, 0x0c+uriextoffset, uriextlen)