From 6607fdc58642437b2ef505c4be30af7569972c1b Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Thu, 18 Sep 2008 17:11:33 -0700 Subject: [PATCH] CLI: add 'tahoe admin generate-keypair' command --- src/allmydata/scripts/admin.py | 66 +++++++++++++++++++++++++++++++++ src/allmydata/scripts/runner.py | 5 ++- src/allmydata/test/test_cli.py | 33 ++++++++++++++++- 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 src/allmydata/scripts/admin.py diff --git a/src/allmydata/scripts/admin.py b/src/allmydata/scripts/admin.py new file mode 100644 index 00000000..119f8601 --- /dev/null +++ b/src/allmydata/scripts/admin.py @@ -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, + } diff --git a/src/allmydata/scripts/runner.py b/src/allmydata/scripts/runner.py index 01aed0eb..39a849e6 100644 --- a/src/allmydata/scripts/runner.py +++ b/src/allmydata/scripts/runner.py @@ -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: diff --git a/src/allmydata/test/test_cli.py b/src/allmydata/test/test_cli.py index 464a597a..f6f20f73 100644 --- a/src/allmydata/test/test_cli.py +++ b/src/allmydata/test/test_cli.py @@ -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 -- 2.45.2