[Pkg-privacy-commits] [txtorcon] 68/96: parser and test for client_keys files

Jérémy Bobbio lunar at moszumanska.debian.org
Sun Sep 6 18:33:41 UTC 2015


This is an automated email from the git hooks/post-receive script.

lunar pushed a commit to branch master
in repository txtorcon.

commit 7166008a55d0098220aa92019b1cc53e8c1787bd
Author: meejah <meejah at meejah.ca>
Date:   Fri Feb 13 00:22:15 2015 -0700

    parser and test for client_keys files
---
 test/test_torconfig.py |  42 +++++++++++++++++
 txtorcon/torconfig.py  | 121 +++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 159 insertions(+), 4 deletions(-)

diff --git a/test/test_torconfig.py b/test/test_torconfig.py
index a431a1e..595d50c 100644
--- a/test/test_torconfig.py
+++ b/test/test_torconfig.py
@@ -1478,3 +1478,45 @@ class ErrorTests(unittest.TestCase):
             pass  # success!
 
         return d
+
+
+# the RSA keys have been shortened below for readability
+keydata = '''client-name bar
+descriptor-cookie O4rQyZ+IJr2PNHUdeXi0nA==
+client-key
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC1R/bPGTWnpGJpNCfT1KIfFq1QEGHz4enKSEKUDkz1CSEPOMGS
+bV37dfqTuI4klsFvdUsR3NpYXLin9xRWvw1viKwAN0y8cv5totl4qMxO5i+zcfVh
+bJiNvVv2EjfEyQaZfAy2PUfp/tAPYZMsyfps2DptWyNR
+-----END RSA PRIVATE KEY-----
+client-name foo
+descriptor-cookie btlj4+RsWEkxigmlszInhQ==
+client-key
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDdLdHU1fbABtFutOFtpdWQdv/9qG1OAc0r1TfaBtkPSNcLezcx
+SThalIEnRFfejy0suOHmsqspruvn0FEflIEQvFWeXAPvXg==
+-----END RSA PRIVATE KEY-----
+client-name quux
+descriptor-cookie asdlkjasdlfkjalsdkfffj==
+'''
+
+class HiddenServiceAuthTests(unittest.TestCase):
+
+    def test_parse_client_keys(self):
+        data = StringIO(keydata)
+        from txtorcon.torconfig import parse_client_keys
+
+        clients = list(parse_client_keys(data))
+
+        self.assertEqual(3, len(clients))
+        self.assertEqual('bar', clients[0].name)
+        self.assertEqual('O4rQyZ+IJr2PNHUdeXi0nA==', clients[0].cookie)
+        self.assertEqual('MIICXQIBAAKBgQC1R/bPGTWnpGJpNCfT1KIfFq1QEGHz4enKSEKUDkz1CSEPOMGSbV37dfqTuI4klsFvdUsR3NpYXLin9xRWvw1viKwAN0y8cv5totl4qMxO5i+zcfVhbJiNvVv2EjfEyQaZfAy2PUfp/tAPYZMsyfps2DptWyNR', clients[0].key)
+
+        self.assertEqual('foo', clients[1].name)
+        self.assertEqual('btlj4+RsWEkxigmlszInhQ==', clients[1].cookie)
+        self.assertEqual(clients[1].key, 'MIICXgIBAAKBgQDdLdHU1fbABtFutOFtpdWQdv/9qG1OAc0r1TfaBtkPSNcLezcxSThalIEnRFfejy0suOHmsqspruvn0FEflIEQvFWeXAPvXg==')
+
+        self.assertEqual('quux', clients[2].name)
+        self.assertEqual('asdlkjasdlfkjalsdkfffj==', clients[2].cookie)
+        self.assertEqual(None, clients[2].key)
diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py
index ef3cfe5..b8d1f89 100644
--- a/txtorcon/torconfig.py
+++ b/txtorcon/torconfig.py
@@ -723,13 +723,126 @@ class HiddenService(object):
             rtn.append(('HiddenServicePort', str(x)))
         if self.version:
             rtn.append(('HiddenServiceVersion', str(self.version)))
-        if self.authorize_client:
-            rtn.append(
-                ('HiddenServiceAuthorizeClient', str(self.authorize_client))
-            )
+        for authline in self.authorize_client:
+            rtn.append(('HiddenServiceAuthorizeClient', str(authline)))
         return rtn
 
 
+'''
+client-name bar
+descriptor-cookie O4rQyZ+IJr2PNHUdeXi0nA==
+client-key
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC1R/bPGTWnpGJpNCfT1KIfFq1QEGHz4enKSEKUDkz1CSEPOMGS
+bV37dfqTuI4klsFvdUsR3NpYXLin9xRWvw1viKwAN0y8cv5totl4qMxO5i+zcfVh
+oBqwzxFD0JVZVLlZjNYfSlkNnkDPmb7btcluzNETBP4IDo8rb2p2zCQc1wIDAQAB
+AoGAWhgg7n5N7zpAep6kKKAlzqObkQ4DUIz3f0P4atLMpn9aAdGoSpi2O7I/zcjM
+RBz0l+tIWuFTVtUGJNwkLJSZHP3FDkoZtOv1JDRD0cbnVClHQ6cOW7Asn/VhOiud
+0yTMkytN2wMpi/vpcQTs28yQdNi1486xPEx8/WEQH2azLJECQQDmkQ18+2gp0eBS
+AdW+9WPsd9RhwKKmDKCJ2TUMN+/4u1WLMx2pARuHqKKOvrWP7vlv7epEoKqWFajc
+3M4B4oupAkEAyUckoM4SOi1YNky9DqFUXrZ1Bl2UVVv15J0BB4jMnYFoSjirVn+i
+4cYHwOBPsvUdkW6OQWvn3gc69Dj5ht60fwJABPFlJaHKKONb+MYbS/28x6wF/JaB
+pietJWdnsLxeQ6nYeqR85UulyHHAEmlaQRyxcknadOAw6AjM1vdQ3095CQJBAKGG
+LzXAyc3YL887nAiOnTd4sscN+AjcSKNS/819Eb9gZ0IQ4icDPMJ0eiplmG/j1vur
+vgtwPd3m7X+p7U03kZ8CQQCHS0lciMezXoDVoi7uljnsFFbbB0HMNXKxcsxym1eQ
+bJiNvVv2EjfEyQaZfAy2PUfp/tAPYZMsyfps2DptWyNR
+-----END RSA PRIVATE KEY-----
+'''
+
+def parse_rsa_blob(lines):
+    return ''.join(lines[1:-1])
+
+
+class HiddenServiceClientAuth(object):
+    def __init__(self, name, cookie, key=None):
+        self.name = name
+        self.cookie = cookie
+        self.key = parse_rsa_blob(key) if key else None
+
+
+def parse_client_keys(stream):
+    '''
+    This parses a hidden-service "client_keys" file, either stealth or
+    basic (they're the same, except "stealth" includes a
+    "client-key"). Returns a list of HiddenServiceClientAuth() instances.
+
+    Note that the key does NOT include the "----BEGIN ---" markers,
+    nor *any* embedded whitespace. It is *just* the key blob.
+
+    '''
+
+    def parse_error(data):
+        raise RuntimeError("Parse error at: " + data)
+
+    class ParserState(object):
+        def __init__(self):
+            self.keys = []
+            self.reset()
+
+        def reset(self):
+            self.name = None
+            self.cookie = None
+            self.key = []
+
+        def create_key(self):
+            if self.name != None:
+                self.keys.append(HiddenServiceClientAuth(self.name, self.cookie, self.key))
+            self.reset()
+
+        def set_name(self, name):
+            self.create_key()
+            self.name = name.split()[1]
+
+        def set_cookie(self, cookie):
+            self.cookie = cookie.split()[1]
+
+        def add_key_line(self, line):
+            self.key.append(line)
+
+
+    from txtorcon.spaghetti import FSM, State, Transition
+    init = State('init')
+    got_name = State('got_name')
+    got_cookie = State('got_cookie')
+    reading_key = State('got_key')
+
+    parser_state = ParserState()
+
+    # initial state; we want "client-name" or it's an error
+    init.add_transitions([
+        Transition(got_name, lambda line: line.startswith('client-name '), parser_state.set_name),
+        Transition(init, lambda line: not line.startswith('client-name '), parse_error),
+    ])
+
+    # next up is "descriptor-cookie" or it's an error
+    got_name.add_transitions([
+        Transition(got_cookie, lambda line: line.startswith('descriptor-cookie '), parser_state.set_cookie),
+        Transition(init, lambda line: not line.startswith('descriptor-cookie '), parse_error),
+    ])
+
+    # the "interesting bit": there's either a client-name if we're a
+    # "basic" file, or an RSA key (with "client-key" before it)
+    got_cookie.add_transitions([
+        Transition(reading_key, lambda line: line.startswith('client-key'), None),
+        Transition(got_name, lambda line: line.startswith('client-name '), parser_state.set_name),
+    ])
+
+    # if we're reading an RSA key, we accumulate it in current_key.key
+    # until we hit a line starting with "client-name"
+    reading_key.add_transitions([
+        Transition(reading_key, lambda line: not line.startswith('client-name'), parser_state.add_key_line),
+        Transition(got_name, lambda line: line.startswith('client-name '), parser_state.set_name),
+    ])
+
+    # create our FSM and parse the data
+    fsm = FSM([init, got_name, got_cookie, reading_key])
+    for line in stream.readlines():
+        fsm.process(line.strip())
+
+    parser_state.create_key()  # make sure we get the "last" one
+    return parser_state.keys
+
+
 class TorConfig(object):
     """This class abstracts out Tor's config, and can be used both to
     create torrc files from nothing and track live configuration of a Tor

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/txtorcon.git



More information about the Pkg-privacy-commits mailing list