]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
CLI: add 'tahoe admin generate-keypair' command
authorBrian Warner <warner@lothar.com>
Fri, 19 Sep 2008 00:11:33 +0000 (17:11 -0700)
committerBrian Warner <warner@lothar.com>
Fri, 19 Sep 2008 00:11:33 +0000 (17:11 -0700)
src/allmydata/scripts/admin.py [new file with mode: 0644]
src/allmydata/scripts/runner.py
src/allmydata/test/test_cli.py

diff --git a/src/allmydata/scripts/admin.py b/src/allmydata/scripts/admin.py
new file mode 100644 (file)
index 0000000..119f860
--- /dev/null
@@ -0,0 +1,66 @@
+
+from twisted.python import usage
+
+class GenerateKeypairOptions(usage.Options):
+    def getSynopsis(self):
+        return "Usage: tahoe admin generate-keypair"
+
+    def getUsage(self, width=None):
+        t = usage.Options.getUsage(self, width)
+        t += """
+Generate an ECDSA192 public/private keypair, dumped to stdout as two lines of
+base32-encoded text.
+
+"""
+        return t
+
+def generate_keypair(options):
+    from pycryptopp.publickey import ecdsa
+    from allmydata.util import base32
+    out = options.stdout
+    privkey = ecdsa.generate(192)
+    print >>out, "private: priv-v0-%s" % base32.b2a(privkey.serialize())
+    pubkey = privkey.get_verifying_key()
+    print >>out, "public: pub-v0-%s" % base32.b2a(pubkey.serialize())
+
+class AdminCommand(usage.Options):
+    subCommands = [
+        ["generate-keypair", None, GenerateKeypairOptions,
+         "Generate a public/private keypair, write to stdout."],
+        ]
+    def postOptions(self):
+        if not hasattr(self, 'subOptions'):
+            raise usage.UsageError("must specify a subcommand")
+    def getSynopsis(self):
+        return "Usage: tahoe admin SUBCOMMAND"
+    def getUsage(self, width=None):
+        #t = usage.Options.getUsage(self, width)
+        t = """
+Subcommands:
+    tahoe admin generate-keypair    Generate a public/private keypair,
+                                    write to stdout.
+
+Please run e.g. 'tahoe admin generate-keypair --help' for more details on
+each subcommand.
+"""
+        return t
+
+subDispatch = {
+    "generate-keypair": generate_keypair,
+    }
+
+def do_admin(options):
+    so = options.subOptions
+    so.stdout = options.stdout
+    so.stderr = options.stderr
+    f = subDispatch[options.subCommand]
+    return f(so)
+
+
+subCommands = [
+    ["admin", None, AdminCommand, "admin subcommands: use 'tahoe admin' for a list"],
+    ]
+
+dispatch = {
+    "admin": do_admin,
+    }
index 01aed0eb589289c8220c61dfbc319ffab53962f1..39a849e66af2e9b90ba3354132ab17cb4caaed20 100644 (file)
@@ -4,12 +4,13 @@ from cStringIO import StringIO
 from twisted.python import usage
 
 from allmydata.scripts.common import BaseOptions
-import debug, create_node, startstop_node, cli, keygen
+import debug, create_node, startstop_node, cli, keygen, admin
 
 _general_commands = ( create_node.subCommands
                     + keygen.subCommands
                     + debug.subCommands
                     + cli.subCommands
+                    + admin.subCommands
                     )
 
 class Options(BaseOptions, usage.Options):
@@ -69,6 +70,8 @@ def runner(argv,
         rc = startstop_node.dispatch[command](so, stdout, stderr)
     elif command in debug.dispatch:
         rc = debug.dispatch[command](so)
+    elif command in admin.dispatch:
+        rc = admin.dispatch[command](so)
     elif command in cli.dispatch:
         rc = cli.dispatch[command](so)
     elif command in keygen.dispatch:
index 464a597aefde1220761cb5e743ab6d4ec1accb90..f6f20f73173beccde7e97e59df15ac88d7d99ae0 100644 (file)
@@ -4,7 +4,8 @@ from twisted.trial import unittest
 from cStringIO import StringIO
 import urllib
 
-from allmydata.util import fileutil, hashutil
+from pycryptopp.publickey import ecdsa
+from allmydata.util import fileutil, hashutil, base32
 from allmydata import uri
 
 # at least import the CLI scripts, even if we don't have any real tests for
@@ -500,5 +501,35 @@ class Put(SystemTestMixin, CLITestMixin, unittest.TestCase):
         d.addCallback(lambda (out,err): self.failUnlessEqual(out, DATA2))
         return d
 
+class Admin(unittest.TestCase):
+    def do_cli(self, *args, **kwargs):
+        argv = list(args)
+        stdin = kwargs.get("stdin", "")
+        stdout, stderr = StringIO(), StringIO()
+        d = threads.deferToThread(runner.runner, argv, run_by_human=False,
+                                  stdin=StringIO(stdin),
+                                  stdout=stdout, stderr=stderr)
+        def _done(res):
+            return stdout.getvalue(), stderr.getvalue()
+        d.addCallback(_done)
+        return d
 
+    def test_generate_keypair(self):
+        d = self.do_cli("admin", "generate-keypair")
+        def _done( (stdout, stderr) ):
+            lines = stdout.split("\n")
+            privkey_line = lines[0].strip()
+            pubkey_line = lines[1].strip()
+            sk_header = "private: priv-v0-"
+            vk_header = "public: pub-v0-"
+            self.failUnless(privkey_line.startswith(sk_header), privkey_line)
+            self.failUnless(pubkey_line.startswith(vk_header), pubkey_line)
+            privkey_b = base32.a2b(privkey_line[len(sk_header):])
+            pubkey_b = base32.a2b(pubkey_line[len(vk_header):])
+            sk = ecdsa.create_signing_key_from_string(privkey_b)
+            vk = ecdsa.create_verifying_key_from_string(pubkey_b)
+            self.failUnlessEqual(sk.get_verifying_key().serialize(),
+                                 vk.serialize())
+        d.addCallback(_done)
+        return d