]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
add 'tahoe dump-cap' command, to show storage index, lease secrets, etc
authorBrian Warner <warner@allmydata.com>
Mon, 14 Jan 2008 20:43:25 +0000 (13:43 -0700)
committerBrian Warner <warner@allmydata.com>
Mon, 14 Jan 2008 20:43:25 +0000 (13:43 -0700)
src/allmydata/scripts/debug.py
src/allmydata/test/test_cli.py

index aa3027993d9c133b312762aa9ae0f349023f5e07..d2c3365a1415ec929099f38d22b3558c1a017779 100644 (file)
@@ -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,
     }
index a1e19b72f1046b04a5e91c7bbb564564810bf57a..60a7c2ecb6b1a8ffb885174e6df6398a51c643fe 100644 (file)
@@ -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)
+