From: Brian Warner Date: Mon, 14 Jan 2008 20:43:25 +0000 (-0700) Subject: add 'tahoe dump-cap' command, to show storage index, lease secrets, etc X-Git-Url: https://git.rkrishnan.org/specifications/(%5B%5E?a=commitdiff_plain;h=6ca0efeef69d108a436d0e411b8b4e1d4ef19fd7;p=tahoe-lafs%2Ftahoe-lafs.git add 'tahoe dump-cap' command, to show storage index, lease secrets, etc --- diff --git a/src/allmydata/scripts/debug.py b/src/allmydata/scripts/debug.py index aa302799..d2c3365a 100644 --- a/src/allmydata/scripts/debug.py +++ b/src/allmydata/scripts/debug.py @@ -1,7 +1,7 @@ # do not import any allmydata modules at this level. Do that from inside # individual functions instead. -import sys, struct, time +import sys, struct, time, os from twisted.python import usage class DumpOptions(usage.Options): @@ -187,11 +187,135 @@ def dump_SDMF_share(offset, length, config, out, err): print >>out + +class DumpCapOptions(usage.Options): + optParameters = [ + ["nodeid", "n", None, "storage server nodeid (ascii), to construct WE and secrets."], + ["client-secret", "c", None, "client's base secret (ascii), to construct secrets"], + ["client-dir", "d", None, "client's base directory, from which a -c secret will be read"], + ] + def parseArgs(self, cap): + self.cap = cap + +def dump_cap(config, out=sys.stdout, err=sys.stderr): + from allmydata import uri + from allmydata.util.idlib import a2b + from base64 import b32decode + + cap = config.cap + u = uri.from_string(cap) + nodeid = None + if config['nodeid']: + nodeid = b32decode(config['nodeid'].upper()) + secret = None + if config['client-secret']: + secret = a2b(config['client-secret']) + elif config['client-dir']: + secretfile = os.path.join(config['client-dir'], "private", "secret") + try: + secret = a2b(open(secretfile, "r").read().strip()) + except EnvironmentError: + pass + + print >>out + dump_uri_instance(u, nodeid, secret, out, err) + +def _dump_secrets(storage_index, secret, nodeid, out): + from allmydata.util import hashutil + from allmydata.util.idlib import b2a + + if secret: + crs = hashutil.my_renewal_secret_hash(secret) + print >>out, " client renewal secret:", b2a(crs) + frs = hashutil.file_renewal_secret_hash(crs, storage_index) + print >>out, " file renewal secret:", b2a(frs) + if nodeid: + renew = hashutil.bucket_renewal_secret_hash(frs, nodeid) + print >>out, " lease renewal secret:", b2a(renew) + ccs = hashutil.my_cancel_secret_hash(secret) + print >>out, " client cancel secret:", b2a(ccs) + fcs = hashutil.file_cancel_secret_hash(ccs, storage_index) + print >>out, " file cancel secret:", b2a(fcs) + if nodeid: + cancel = hashutil.bucket_cancel_secret_hash(fcs, nodeid) + print >>out, " lease cancel secret:", b2a(cancel) + +def dump_uri_instance(u, nodeid, secret, out, err, show_header=True): + from allmydata import uri + from allmydata.util.idlib import b2a + from allmydata.util import hashutil + + if isinstance(u, uri.CHKFileURI): + if show_header: + print >>out, "CHK File:" + print >>out, " key:", b2a(u.key) + print >>out, " UEB hash:", b2a(u.uri_extension_hash) + print >>out, " size:", u.size + print >>out, " k/N: %d/%d" % (u.needed_shares, u.total_shares) + print >>out, " storage index:", b2a(u.storage_index) + _dump_secrets(u.storage_index, secret, nodeid, out) + elif isinstance(u, uri.CHKFileVerifierURI): + if show_header: + print >>out, "CHK Verifier URI:" + print >>out, " UEB hash:", b2a(u.uri_extension_hash) + print >>out, " size:", u.size + print >>out, " k/N: %d/%d" % (u.needed_shares, u.total_shares) + print >>out, " storage index:", b2a(u.storage_index) + + elif isinstance(u, uri.LiteralFileURI): + if show_header: + print >>out, "Literal File URI:" + print >>out, " data:", u.data + + elif isinstance(u, uri.WriteableSSKFileURI): + if show_header: + print >>out, "SSK Writeable URI:" + print >>out, " writekey:", b2a(u.writekey) + print >>out, " readkey:", b2a(u.readkey) + print >>out, " storage index:", b2a(u.storage_index) + print >>out, " fingerprint:", b2a(u.fingerprint) + print >>out + if nodeid: + we = hashutil.ssk_write_enabler_hash(u.writekey, nodeid) + print >>out, " write_enabler:", b2a(we) + print >>out + _dump_secrets(u.storage_index, secret, nodeid, out) + + elif isinstance(u, uri.ReadonlySSKFileURI): + if show_header: + print >>out, "SSK Read-only URI:" + print >>out, " readkey:", b2a(u.readkey) + print >>out, " storage index:", b2a(u.storage_index) + print >>out, " fingerprint:", b2a(u.fingerprint) + elif isinstance(u, uri.SSKVerifierURI): + if show_header: + print >>out, "SSK Verifier URI:" + print >>out, " storage index:", b2a(u.storage_index) + print >>out, " fingerprint:", b2a(u.fingerprint) + + elif isinstance(u, uri.NewDirectoryURI): + if show_header: + print >>out, "Directory Writeable URI:" + dump_uri_instance(u._filenode_uri, nodeid, secret, out, err, False) + elif isinstance(u, uri.ReadonlyNewDirectoryURI): + if show_header: + print >>out, "Directory Read-only URI:" + dump_uri_instance(u._filenode_uri, nodeid, secret, out, err, False) + elif isinstance(u, uri.NewDirectoryURIVerifier): + if show_header: + print >>out, "Directory Verifier URI:" + dump_uri_instance(u._filenode_uri, nodeid, secret, out, err, False) + else: + print >>out, "unknown cap type" + + subCommands = [ ["dump-share", None, DumpOptions, "Unpack and display the contents of a share (uri_extension and leases)."], + ["dump-cap", None, DumpCapOptions, "Unpack a read-cap or write-cap"] ] dispatch = { "dump-share": dump_share, + "dump-cap": dump_cap, } diff --git a/src/allmydata/test/test_cli.py b/src/allmydata/test/test_cli.py index a1e19b72..60a7c2ec 100644 --- a/src/allmydata/test/test_cli.py +++ b/src/allmydata/test/test_cli.py @@ -1,15 +1,17 @@ from twisted.trial import unittest +from cStringIO import StringIO -from allmydata.util import fileutil +from allmydata.util import fileutil, hashutil from allmydata import uri # at least import the CLI scripts, even if we don't have any real tests for # them yet. - -from allmydata.scripts import cli, tahoe_ls, tahoe_get, tahoe_put, tahoe_rm +from allmydata.scripts import tahoe_ls, tahoe_get, tahoe_put, tahoe_rm _hush_pyflakes = [tahoe_ls, tahoe_get, tahoe_put, tahoe_rm] +from allmydata.scripts import cli, debug + class CLI(unittest.TestCase): def test_options(self): @@ -62,3 +64,145 @@ class CLI(unittest.TestCase): self.failUnlessEqual(o['node-url'], "http://localhost:8080/") self.failUnlessEqual(o['dir-cap'], other_uri) self.failUnlessEqual(o['vdrive_pathname'], "subdir") + + def _dump_cap(self, *args): + out,err = StringIO(), StringIO() + config = debug.DumpCapOptions() + config.parseOptions(args) + debug.dump_cap(config, out, err) + self.failIf(err.getvalue()) + output = out.getvalue() + return output + + def test_dump_cap_chk(self): + key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + storage_index = hashutil.storage_index_chk_hash(key) + uri_extension_hash = hashutil.uri_extension_hash("stuff") + needed_shares = 25 + total_shares = 100 + size = 1234 + u = uri.CHKFileURI(key=key, + uri_extension_hash=uri_extension_hash, + needed_shares=needed_shares, + total_shares=total_shares, + size=size) + output = self._dump_cap(u.to_string()) + self.failUnless("CHK File:" in output) + self.failUnless("key: yyyoryarywdyqnyjbefoadeqbh" in output) + self.failUnless("UEB hash: hd7rwri6djiapo6itg5hcxa7ze5im7z9qwcdu8oka6qinahsbiuo" in output) + self.failUnless("size: 1234" in output) + self.failUnless("k/N: 25/100" in output) + self.failUnless("storage index: p3w849k9whqhw6b9fkf4xjs5xc" in output) + + output = self._dump_cap("--client-secret", "p3w849k9whqhw6b9fkf4xjs5xc", + u.to_string()) + self.failUnless("client renewal secret: pu3oy5fu4irjsudwhn6c71g87anrxi1kokt4hmxz7qh5p1895zpy" in output) + + output = self._dump_cap(u.get_verifier().to_string()) + self.failIf("key: " in output) + self.failUnless("UEB hash: hd7rwri6djiapo6itg5hcxa7ze5im7z9qwcdu8oka6qinahsbiuo" in output) + self.failUnless("size: 1234" in output) + self.failUnless("k/N: 25/100" in output) + self.failUnless("storage index: p3w849k9whqhw6b9fkf4xjs5xc" in output) + + def test_dump_cap_lit(self): + u = uri.LiteralFileURI("this is some data") + output = self._dump_cap(u.to_string()) + self.failUnless("Literal File URI:" in output) + self.failUnless("data: this is some data" in output) + + def test_dump_cap_ssk(self): + writekey = "\x01" * 16 + fingerprint = "\xfe" * 32 + u = uri.WriteableSSKFileURI(writekey, fingerprint) + + output = self._dump_cap(u.to_string()) + self.failUnless("SSK Writeable URI:" in output) + self.failUnless("writekey: yryonyebyryonyebyryonyebyr" in output) + self.failUnless("readkey: zhgqsyrkuywo3rha41b1d7xrar" in output) + self.failUnless("storage index: toz9zpxrzjzwoxtuq6xr3ygdze" in output) + self.failUnless("fingerprint: 959x79z6959x79z6959x79z6959x79z6959x79z6959x79z6959y" in output) + + output = self._dump_cap("--client-secret", "p3w849k9whqhw6b9fkf4xjs5xc", + u.to_string()) + self.failUnless("file renewal secret: xy9p89q9pkitqn4ycwu5tpt9yia7s9izsqudnb4q5jdc3rawgcny" in output) + + fileutil.make_dirs("cli/test_dump_cap/private") + f = open("cli/test_dump_cap/private/secret", "w") + f.write("p3w849k9whqhw6b9fkf4xjs5xc\n") + f.close() + output = self._dump_cap("--client-dir", "cli/test_dump_cap", + u.to_string()) + self.failUnless("file renewal secret: xy9p89q9pkitqn4ycwu5tpt9yia7s9izsqudnb4q5jdc3rawgcny" in output) + + output = self._dump_cap("--client-dir", "cli/test_dump_cap_BOGUS", + u.to_string()) + self.failIf("file renewal secret:" in output) + + output = self._dump_cap("--nodeid", "tqc35esocrvejvg4mablt6aowg6tl43j", + u.to_string()) + self.failUnless("write_enabler: rqk9q6w46dim5ybshqk9kotkyhqcdqmp1z6498xniuz5kkjs1w7o" in output) + self.failIf("file renewal secret:" in output) + + output = self._dump_cap("--nodeid", "tqc35esocrvejvg4mablt6aowg6tl43j", + "--client-secret", "p3w849k9whqhw6b9fkf4xjs5xc", + u.to_string()) + self.failUnless("write_enabler: rqk9q6w46dim5ybshqk9kotkyhqcdqmp1z6498xniuz5kkjs1w7o" in output) + self.failUnless("file renewal secret: xy9p89q9pkitqn4ycwu5tpt9yia7s9izsqudnb4q5jdc3rawgcny" in output) + self.failUnless("lease renewal secret: r3fsw67mfji3c9mtsisqdumc1pz3gquzdrh4cpu63h8du4uuedgo" in output) + + u = u.get_readonly() + output = self._dump_cap(u.to_string()) + self.failUnless("SSK Read-only URI:" in output) + self.failUnless("readkey: zhgqsyrkuywo3rha41b1d7xrar" in output) + self.failUnless("storage index: toz9zpxrzjzwoxtuq6xr3ygdze" in output) + self.failUnless("fingerprint: 959x79z6959x79z6959x79z6959x79z6959x79z6959x79z6959y" in output) + + u = u.get_verifier() + output = self._dump_cap(u.to_string()) + self.failUnless("SSK Verifier URI:" in output) + self.failUnless("storage index: toz9zpxrzjzwoxtuq6xr3ygdze" in output) + self.failUnless("fingerprint: 959x79z6959x79z6959x79z6959x79z6959x79z6959x79z6959y" in output) + + def test_dump_cap_directory(self): + writekey = "\x01" * 16 + fingerprint = "\xfe" * 32 + u1 = uri.WriteableSSKFileURI(writekey, fingerprint) + u = uri.NewDirectoryURI(u1) + + output = self._dump_cap(u.to_string()) + self.failUnless("Directory Writeable URI:" in output) + self.failUnless("writekey: yryonyebyryonyebyryonyebyr" in output) + self.failUnless("readkey: zhgqsyrkuywo3rha41b1d7xrar" in output) + self.failUnless("storage index: toz9zpxrzjzwoxtuq6xr3ygdze" in output) + self.failUnless("fingerprint: 959x79z6959x79z6959x79z6959x79z6959x79z6959x79z6959y" in output) + + output = self._dump_cap("--client-secret", "p3w849k9whqhw6b9fkf4xjs5xc", + u.to_string()) + self.failUnless("file renewal secret: xy9p89q9pkitqn4ycwu5tpt9yia7s9izsqudnb4q5jdc3rawgcny" in output) + + output = self._dump_cap("--nodeid", "tqc35esocrvejvg4mablt6aowg6tl43j", + u.to_string()) + self.failUnless("write_enabler: rqk9q6w46dim5ybshqk9kotkyhqcdqmp1z6498xniuz5kkjs1w7o" in output) + self.failIf("file renewal secret:" in output) + + output = self._dump_cap("--nodeid", "tqc35esocrvejvg4mablt6aowg6tl43j", + "--client-secret", "p3w849k9whqhw6b9fkf4xjs5xc", + u.to_string()) + self.failUnless("write_enabler: rqk9q6w46dim5ybshqk9kotkyhqcdqmp1z6498xniuz5kkjs1w7o" in output) + self.failUnless("file renewal secret: xy9p89q9pkitqn4ycwu5tpt9yia7s9izsqudnb4q5jdc3rawgcny" in output) + self.failUnless("lease renewal secret: r3fsw67mfji3c9mtsisqdumc1pz3gquzdrh4cpu63h8du4uuedgo" in output) + + u = u.get_readonly() + output = self._dump_cap(u.to_string()) + self.failUnless("Directory Read-only URI:" in output) + self.failUnless("readkey: zhgqsyrkuywo3rha41b1d7xrar" in output) + self.failUnless("storage index: toz9zpxrzjzwoxtuq6xr3ygdze" in output) + self.failUnless("fingerprint: 959x79z6959x79z6959x79z6959x79z6959x79z6959x79z6959y" in output) + + u = u.get_verifier() + output = self._dump_cap(u.to_string()) + self.failUnless("Directory Verifier URI:" in output) + self.failUnless("storage index: toz9zpxrzjzwoxtuq6xr3ygdze" in output) + self.failUnless("fingerprint: 959x79z6959x79z6959x79z6959x79z6959x79z6959x79z6959y" in output) +