[Pkg-privacy-commits] [obfsproxy] 73/353: Fix obfs2.

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 13:01:41 UTC 2015


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

infinity0 pushed a commit to branch master
in repository obfsproxy.

commit bd9247951d8ac308f26783f1c78da5156d2b6f01
Author: George Kadianakis <desnacked at riseup.net>
Date:   Wed Oct 3 17:01:03 2012 +0300

    Fix obfs2.
---
 obfsproxy/common/aes.py        |  51 ++-----
 obfsproxy/managed/client.py    |   2 +-
 obfsproxy/managed/server.py    |   2 +-
 obfsproxy/test/aes_unittest.py |  81 ++++++++++
 obfsproxy/test/tester.py       |  12 +-
 obfsproxy/transports/obfs2.py  | 335 ++++++++++++++++++-----------------------
 6 files changed, 252 insertions(+), 231 deletions(-)

diff --git a/obfsproxy/common/aes.py b/obfsproxy/common/aes.py
index ccddeb0..43e0e4a 100644
--- a/obfsproxy/common/aes.py
+++ b/obfsproxy/common/aes.py
@@ -5,47 +5,22 @@
 
 from Crypto.Cipher import AES
 from Crypto.Util import Counter
-import base64
-import os
 
-# the block size for the cipher object; must be 16, 24, or 32 for AES
+class AES_CTR_128(object):
+    """An AES-CTR-128 PyCrypto wrapper."""
 
-BLOCK_SIZE = 32
+    def __init__(self, key, iv):
+        """Initialize AES with the given key and IV."""
 
-# the character used for padding--with a block cipher such as AES, the value
-# you encrypt must be a multiple of BLOCK_SIZE in length.  This character is
-# used to ensure that your value is always a multiple of BLOCK_SIZE
+        assert(len(key) == 16)
+        assert(len(iv) == 16)
 
-PADDING = '{'
-
-# one-liner to sufficiently pad the text to be encrypted
-
-pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
-
-
-class AESCoder(object):
-
-    """ This class a convenience wrapper for the AES cipher in CTR mode. """
-
-    def __init__(self, key):
-        """ Initialize AES with the given key and two counters, one for encryption and one for decryption. """
-
-        counterIn = Counter.new(128)
-        self.cipherIn = AES.new(key, mode=AES.MODE_CTR,
-                                counter=counterIn)
-
-        counterOut = Counter.new(128)
-        self.cipherOut = AES.new(key, mode=AES.MODE_CTR,
-                                 counter=counterOut)
-
-    def encrypt(self, data):
-        """ Encrypt the given data using AES in CTR mode and the key specified in __init__. """
-
-        return self.cipherOut.encrypt(pad(data))
-
-    def decrypt(self, data):
-        """ Decrypt the given data using AES in CTR mode and the key specified in __init__. """
-
-        return self.cipherIn.decrypt(data).rstrip(PADDING)
+        self.ctr = Counter.new(128, initial_value=long(iv.encode('hex'), 16)) # XXX hex???
+        self.cipher = AES.new(key, AES.MODE_CTR, counter=self.ctr)
 
+    def crypt(self, data):
+        """
+        Encrypt or decrypt 'data'.
+        """
+        return self.cipher.encrypt(data)
 
diff --git a/obfsproxy/managed/client.py b/obfsproxy/managed/client.py
index 3f6a2ee..cf97943 100644
--- a/obfsproxy/managed/client.py
+++ b/obfsproxy/managed/client.py
@@ -54,7 +54,7 @@ class ManagedClient:
             log.error("Could not find transport class for '%s' (%s)." % (name, 'client'))
             return False, None
 
-        factory = socks.SOCKSv4Factory(clientClass())
+        factory = socks.SOCKSv4Factory(clientClass)
 
         try:
             addrport = reactor.listenTCP(0, factory, interface='localhost')
diff --git a/obfsproxy/managed/server.py b/obfsproxy/managed/server.py
index a4b999f..17c3795 100644
--- a/obfsproxy/managed/server.py
+++ b/obfsproxy/managed/server.py
@@ -53,7 +53,7 @@ class ManagedServer:
             log.error("Could not find transport class for '%s' (%s)." % (name, 'server'))
             return False, None
 
-        factory = network.StaticDestinationServerFactory(managedInfo['orport'], 'server', serverClass())
+        factory = network.StaticDestinationServerFactory(managedInfo['orport'], 'server', serverClass)
 
         try:
             addrport = reactor.listenTCP(int(bindaddr[1]), factory)
diff --git a/obfsproxy/test/aes_unittest.py b/obfsproxy/test/aes_unittest.py
new file mode 100644
index 0000000..6b6ff59
--- /dev/null
+++ b/obfsproxy/test/aes_unittest.py
@@ -0,0 +1,81 @@
+import unittest
+
+from Crypto.Cipher import AES
+from Crypto.Util import Counter
+
+import obfsproxy.common.aes as aes
+
+class testAES_CTR_128_NIST(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c"
+        iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+
+        cls.ctr = Counter.new(128, initial_value=long(iv.encode('hex'), 16))
+        cls.cipher = AES.new(key, AES.MODE_CTR, counter=cls.ctr)
+
+    def _helper_test_vector(self, input_block, output_block, plaintext, ciphertext):
+        self.assertEqual(long(input_block.encode('hex'), 16), self.ctr.next_value())
+
+        ct = self.cipher.encrypt(plaintext)
+        self.assertEqual(ct, ciphertext)
+
+        # XXX how do we extract the keystream out of the AES object?
+
+    def test_1(self):
+        input_block = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+        output_block = "\xec\x8c\xdf\x73\x98\x60\x7c\xb0\xf2\xd2\x16\x75\xea\x9e\xa1\xe4"
+        plaintext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+        ciphertext = "\x87\x4d\x61\x91\xb6\x20\xe3\x26\x1b\xef\x68\x64\x99\x0d\xb6\xce"
+
+        self._helper_test_vector(input_block, output_block, plaintext, ciphertext)
+
+    def test_2(self):
+        input_block = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xff\x00"
+        output_block = "\x36\x2b\x7c\x3c\x67\x73\x51\x63\x18\xa0\x77\xd7\xfc\x50\x73\xae"
+        plaintext = "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+        ciphertext = "\x98\x06\xf6\x6b\x79\x70\xfd\xff\x86\x17\x18\x7b\xb9\xff\xfd\xff"
+
+        self._helper_test_vector(input_block, output_block, plaintext, ciphertext)
+
+    def test_3(self):
+        input_block = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xff\x01"
+        output_block = "\x6a\x2c\xc3\x78\x78\x89\x37\x4f\xbe\xb4\xc8\x1b\x17\xba\x6c\x44"
+        plaintext = "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+        ciphertext = "\x5a\xe4\xdf\x3e\xdb\xd5\xd3\x5e\x5b\x4f\x09\x02\x0d\xb0\x3e\xab"
+
+        self._helper_test_vector(input_block, output_block, plaintext, ciphertext)
+
+    def test_4(self):
+        input_block = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xff\x02"
+        output_block = "\xe8\x9c\x39\x9f\xf0\xf1\x98\xc6\xd4\x0a\x31\xdb\x15\x6c\xab\xfe"
+        plaintext = "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10"
+        ciphertext = "\x1e\x03\x1d\xda\x2f\xbe\x03\xd1\x79\x21\x70\xa0\xf3\x00\x9c\xee"
+
+        self._helper_test_vector(input_block, output_block, plaintext, ciphertext)
+
+class testAES_CTR_128_simple(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.key = "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24"
+        cls.iv = "\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55"
+
+    def test_encrypt_decrypt_small_ASCII(self):
+        """
+        Validate that decryption and encryption work as intended on a small ASCII string.
+        """
+
+        test_string = "This unittest kills fascists."
+
+        cipher1 = aes.AES_CTR_128(self.key, self.iv)
+        cipher2 = aes.AES_CTR_128(self.key, self.iv)
+
+        ct = cipher1.crypt(test_string)
+        pt = cipher2.crypt(ct)
+
+        self.assertEqual(test_string, pt)
+
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/obfsproxy/test/tester.py b/obfsproxy/test/tester.py
index 1c912e6..b2d9ea5 100644
--- a/obfsproxy/test/tester.py
+++ b/obfsproxy/test/tester.py
@@ -209,8 +209,8 @@ class DirectTest(object):
 
         report = diff("errors in transfer:", TEST_FILE, output)
 
-        report += self.obfs_client.check_completion("obfsproxy client", report!="")
-        report += self.obfs_server.check_completion("obfsproxy server", report!="")
+        report += self.obfs_client.check_completion("obfsproxy client (%s)" % self.transport, report!="")
+        report += self.obfs_server.check_completion("obfsproxy server (%s)" % self.transport, report!="")
 
         if report != "":
             self.fail("\n" + report)
@@ -220,6 +220,7 @@ class DirectTest(object):
 #
 
 class DirectDummy(DirectTest, unittest.TestCase):
+    transport = "dummy"
     server_args = ("dummy", "server",
                    "127.0.0.1:%d" % SERVER_PORT,
                    "--dest=127.0.0.1:%d" % EXIT_PORT)
@@ -227,11 +228,12 @@ class DirectDummy(DirectTest, unittest.TestCase):
                    "127.0.0.1:%d" % ENTRY_PORT,
                    "--dest=127.0.0.1:%d" % SERVER_PORT)
 
-class DirectB64(DirectTest, unittest.TestCase):
-    server_args = ("b64", "server",
+class DirectObfs2(DirectTest, unittest.TestCase):
+    transport = "obfs2"
+    server_args = ("obfs2", "server",
                    "127.0.0.1:%d" % SERVER_PORT,
                    "--dest=127.0.0.1:%d" % EXIT_PORT)
-    client_args = ("b64", "client",
+    client_args = ("obfs2", "client",
                    "127.0.0.1:%d" % ENTRY_PORT,
                    "--dest=127.0.0.1:%d" % SERVER_PORT)
 
diff --git a/obfsproxy/transports/obfs2.py b/obfsproxy/transports/obfs2.py
index 38593a5..0ee2c10 100644
--- a/obfsproxy/transports/obfs2.py
+++ b/obfsproxy/transports/obfs2.py
@@ -6,31 +6,27 @@ The obfs2 module implements the obfs2 protocol.
 """
 
 import os
-
 import random
-import hmac
 import hashlib
-import b64
+import struct
+
+import obfsproxy.common.aes as aes
+import obfsproxy.transports.base as base
 
-from obfsproxy.common.aes import AESCoder
-from obfsproxy.transports.base import BaseDaemon
+import obfsproxy.common.log as log
 
-MAGIC_VALUE = decode('2BF5CA7E')
+MAGIC_VALUE = 0x2BF5CA7E
 SEED_LENGTH = 16
 MAX_PADDING = 8192
 HASH_ITERATIONS = 100000
 
 KEYLEN = 16  # is the length of the key used by E(K,s) -- that is, 16.
 IVLEN = 16  # is the length of the IV used by E(K,s) -- that is, 16.
-
 HASHLEN = 16  # is the length of the output of H() -- that is, 32.
 
-READ_SEED = 0
-READ_PADKEY = 1
-READ_PADLEN = 2
-READ_PADDING = 3
-STREAM = 4
-
+ST_WAIT_FOR_KEY = 0
+ST_WAIT_FOR_PADDING = 1
+ST_OPEN = 2
 
 def h(x):
     """ H(x) is SHA256 of x. """
@@ -43,218 +39,175 @@ def h(x):
 def hn(x, n):
     """ H^n(x) is H(x) called iteratively n times. """
 
-    data = x
-    for x in range(n):
-        data = h(data)
+    for _ in xrange(n):
+        data = h(x)
     return data
 
-
-def e(k, s):
-    """ E(k,s) is the AES-CTR-128 encryption of s using K as key. """
-
-    cipher = AESCoder(k)
-    return cipher.encode(s)
-
-
-def d(k, s):
-    """ D(k, s) is the AES-CTR-128 decryption of s using K as key. """
-
-    cipher = AESCoder(k)
-    return cipher.decode(s)
-
-
-def uint32(n):
-    """ UINT32(n) is the 4 byte value of n in big-endian (network) order. """
-
+def htonl(n):
     return struct.pack('!I', n)
 
 
-def decodeUint32(bs):
-    """
-    decodeUint32(bs) is the reverse of uint32(n).
-    It returns the int value represneted by the 4 byte big-endian (network) order encoding represented by bs.
-    """
-
+def ntohl(bs):
     return struct.unpack('!I', bs)[0]
 
 
-def sr(n):
-    """ SR(n) is n bytes of strong random data. """
+def random_bytes(n):
+    """ Returns n bytes of strong random data. """
 
     return os.urandom(n)
 
-
-def wr(n):
-    """ WR(n) is n bytes of weaker random data. """
-
-    return ''.join(chr(random.randint(0, 255)) for _ in range(n))
-
-
 def mac(s, x):
     """ # MAC(s, x) = H(s | x | s) """
 
     return h(s + x + s)
 
-class Obfs2Daemon(BaseDaemon):
-
+class Obfs2Daemon(base.BaseDaemon):
     """
     Obfs2Daemon implements the obfs2 protocol.
-    It is subclassed by Obfs2Client and Obfs2Server.
     """
 
-    def __init__(self, downstreamConnection, upstreamConnection):
+    def __init__(self):
+        """Initialize the obfs2 pluggable transport."""
+
+        # Our state.
+        self.state = ST_WAIT_FOR_KEY
+
+        if self.we_are_initiator:
+            self.initiator_seed = random_bytes(SEED_LENGTH) # Initiator's seed.
+            self.responder_seed = None # Responder's seed.
+        else:
+            self.initiator_seed = None # Initiator's seed.
+            self.responder_seed = random_bytes(SEED_LENGTH) # Responder's seed
+
+        # Shared secret seed.
+        self.secret_seed = None
+
+        # Crypto to encrypt outgoing data.
+        self.send_crypto = None
+        # Crypto to encrypt outgoing padding. Generate it now.
+        self.send_padding_crypto = \
+            self._derive_padding_crypto(self.initiator_seed if self.we_are_initiator else self.responder_seed,
+                                     self.send_pad_keytype)
+        # Crypto to decrypt incoming data.
+        self.recv_crypto = None
+        # Crypto to decrypt incoming padding.
+        self.recv_padding_crypto = None
+
+        # Number of padding bytes left to read.
+        self.padding_left_to_read = 0
+
+        # If it's True, it means that we received upstream data before
+        # we had the chance to set up our crypto (after receiving the
+        # handshake). This means that when we set up our crypto, we
+        # must remember to push the cached upstream data downstream.
+        self.pending_data_to_send = False
+
+    def handshake(self, circuit):
         """
-        Initializes the Obfs2Daemon instance with the upstream and downstream connections.
-        Also initializes the seed, padkey, and padlen buffers to be empty byte strings.
+        Do the obfs2 handshake:
+        SEED | E_PAD_KEY( UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN) )
         """
 
-        self.downstreamConnection = downstreamConnection
-        self.upstreamConnection = upstreamConnection
-        self.otherSeed = bytes('')
-        self.otherPadKeyEncrypted = bytes('')
-        self.otherPadLenBytes = bytes('')
+        padding_length = random.randint(0, MAX_PADDING)
+        seed = self.initiator_seed if self.we_are_initiator else self.responder_seed
+
+        handshake_message = seed + self.send_padding_crypto.crypt(htonl(MAGIC_VALUE) + htonl(padding_length) + random_bytes(padding_length))
 
-    def start(self):
+        log.debug("obfs2 handshake: %s queued %d bytes (padding_length: %d).",
+                  "initiator" if self.we_are_initiator else "responder",
+                  len(handshake_message), padding_length)
+
+        circuit.downstream.write(handshake_message)
+
+    def receivedUpstream(self, data, circuit):
         """
-        This is the callback method which is called by the framework when a new connection has been made.
-        In the obfs2 protocol, on start the seed, encrypted magic value, padding length, and padding are written upstream.
+        Got data from upstream. We need to obfuscated and proxy them downstream.
         """
+        if not self.send_crypto:
+            log.debug("Got upstream data before doing handshake. Caching.")
+            self.pending_data_to_send = True
+            return
 
-        # The initiator generates:
-        # INIT_SEED = SR(SEED_LENGTH)
+        log.debug("obfs2 receivedUpstream: Transmitting %d bytes.", len(data))
+        # Encrypt and proxy them.
+        circuit.downstream.write(self.send_crypto.crypt(data.read()))
 
-        self.seed = sr(SEED_LENGTH)
-        self.padKey = self.derivePadKey(self.seed, self.padString)
+    def receivedDownstream(self, data, circuit):
+        """
+        Got data from downstream. We need to de-obfuscate them and
+        proxy them upstream.
+        """
+        log_prefix = "obfs2 receivedDownstream" # used in logs
 
-        # Each then generates a random number PADLEN in range from 0 through MAX_PADDING (inclusive).
+        if self.state == ST_WAIT_FOR_KEY:
+            log.debug("%s: Waiting for key." % log_prefix)
+            if len(data) < SEED_LENGTH + 8:
+                log.debug("%s: Not enough bytes for key (%d)." % (log_prefix, len(data)))
+                return data # incomplete
 
-        self.padLen = random.nextInt(MAX_PADDING) % MAX_PADDING
+            if self.we_are_initiator:
+                self.responder_seed = data.read(SEED_LENGTH)
+            else:
+                self.initiator_seed = data.read(SEED_LENGTH)
 
-        # The initiator then sends:
-        # INIT_SEED | E(INIT_PAD_KEY, UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN))
+            # Now that we got the other seed, let's set up our crypto.
+            self.send_crypto = self._derive_crypto(self.send_keytype)
+            self.recv_crypto = self._derive_crypto(self.recv_keytype)
+            self.recv_padding_crypto = \
+                self._derive_padding_crypto(self.responder_seed if self.we_are_initiator else self.initiator_seed,
+                                            self.recv_pad_keytype)
 
-        self.server.write(self.seed + e(self.padKey,
-                          uint32(MAGIC_VALUE)) + uint32(self.padLen)
-                          + wr(self.padLen))
+            # XXX maybe faster with a single d() instead of two.
+            magic = ntohl(self.recv_padding_crypto.crypt(data.read(4)))
+            padding_length = ntohl(self.recv_padding_crypto.crypt(data.read(4)))
 
-        self.state = READ_SEED
+            log.debug("%s: Got %d bytes of handshake data (padding_length: %d, magic: %s)" % \
+                          (log_prefix, len(data), padding_length, hex(magic)))
 
-    def receivedDownstream(self):
-        """
-        This is the callback method which is called by the framework when bytes have been received on the downstream socket.
-        In the obfs2 protocol, downstream bytes are buffered until the handshake is complete and the protocol is in STREAM mode, at which point all bytes received from downstream are encrypted and sent upstream.
-        """
+            if magic != MAGIC_VALUE:
+                raise base.PluggableTransportError("obfs2: Corrupted magic value '%s'" % hex(magic))
+            if padding_length > MAX_PADDING:
+                raise base.PluggableTransportError("obfs2: Too big padding length '%s'" % padding_length)
 
-        if state == STREAM:
-            data = self.downstreamConnection.read_some()
-            encodedData = e(padKey, data)
-            self.upstreamConnection.write(encodedData)
+            self.padding_left_to_read = padding_length
+            self.state = ST_WAIT_FOR_PADDING
 
-    def receivedUpstream(self):
-        """
-        This is the callback method which is called by the framework when bytes have been received on the upstream socket.
-        In the obfs2 protocol, the upstream data stream is read to find the following values:
-            - seed
-            - padding key
-            - padding length
-            - padding
-        The protocol is then switched to STREAM mode, at which point all bytes received from upstream are encrypted and sent downstream.
-        """
+        while self.padding_left_to_read:
+            if not data: return
 
-        if state == READ_SEED:
-            self.otherSeed = self.read(self.upstreamConnection,
-                    self.otherSeed, SEED_LENGTH)
-            if self.checkTransition(self.otherSeed, SEED_LENGTH,
-                                    READ_PADKEY):
-                self.otherPadKeyDerived = \
-                    self.derivePadKey(self.otherSeed, not self.server)
-        elif state == READ_PADKEY:
-            self.otherPadKeyEncrypted = \
-                self.read(self.upstreamConnection,
-                          self.otherPadKeyEncrypted, KEYLEN)
-            if self.checkTransition(self.otherPadKeyEncrypted, KEYLEN,
-                                    READ_PADLEN):
-                if self.otherPadKeyEncrypted != self.otherPadKeyDerived:
-                    self.upstreamConnection.disconnect()
-                    self.downstreamConnection.disconnect()
-        elif state == READ_PADLEN:
-            self.otherPadLenBytes = self.read(self.upstreamConnection,
-                    self.otherPadLenBytes, 4)
-            if self.checkTransition(self.otherPadLenBytes, 4,
-                                    READ_PADDING):
-                self.otherPadLen = decodeUint32(self.otherPadLenBytes)
-                if self.otherPadLen > MAX_PADDING:
-                    self.upstreamConnection.disconnect()
-                    self.downstreamConnection.disconnect()
-        elif state == READ_PADDING:
-            self.otherPadding = self.read(self.upstreamConnection,
-                    self.otherPadding, self.otherPadLen)
-            if self.checkTransition(self.otherPadding,
-                                    self.otherPadLen, READ_PADDING):
-                self.secret = self.deriveSecret(self.seed,
-                        self.otherSeed, self.server)
-                self.otherSecret = self.deriveSecret(self.otherSeed,
-                        self.seed, not self.server)
-
-                # INIT_KEY = INIT_SECRET[:KEYLEN]
-                # RESP_KEY = RESP_SECRET[:KEYLEN]
-
-                self.key = self.secret[:KEYLEN]
-                self.otherKey = self.otherSecret[:KEYLEN]
-
-                # INIT_IV = INIT_SECRET[KEYLEN:]
-                # RESP_IV = RESP_SECRET[KEYLEN:]
-
-                self.iv = self.secret[KEYLEN:]
-                self.otheriv = self.otherSecret[KEYLEN:]
-
-                self.cipher = initCipher(self.iv, self.key)
-                self.otherCipher = initCipher(self.otheriv,
-                        self.otherKey)
-        elif state == STREAM:
-            data = self.upstreamConnection.read_some()
-            decodedData = d(padKey, data)
-            self.downstreamConnection.write(decodedData)
-
-    def derivePadKey(self, seed, padString):
-        """ derivePadKey returns the MAC of the padString and seed. """
-
-        return mac(padString, seed)[:KEYLEN]
-
-    def deriveSecret(
-        self,
-        seed,
-        otherSeed,
-        server,
-        ):
-        """ deriveSecret returns the MAC of the stored padString, the seed, and the otherSeed. """
-
-        if server:
-
-            # RESP_SECRET = MAC("Responder obfuscated data", INIT_SEED|RESP_SEED)
-
-            return mac(self.padString, otherSeed + seed)
-        else:
+            n_to_drain = self.padding_left_to_read
+            if (self.padding_left_to_read > len(data)):
+                n_to_drain = len(data)
 
-            # INIT_SECRET = MAC("Initiator obfuscated data", INIT_SEED|RESP_SEED)
+            data.drain(n_to_drain)
+            self.padding_left_to_read -= n_to_drain
+            log.debug("%s: Consumed %d bytes of padding, %d still to come (%d).",
+                      log_prefix, n_to_drain, self.padding_left_to_read, len(data))
 
-            return mac(self.padString, seed + otherSeed)
+        self.state = ST_OPEN
+        log.debug("%s: Processing %d bytes of application data.",
+                  log_prefix, len(data))
 
-    def initCipher(self, iv, key):
-        """ initCipher initializes the AES cipher using the given key and IV. """
+        if self.pending_data_to_send:
+            log.debug("%s: We got pending data to send and our crypto is ready. Pushing!" % log_prefix)
+            self.receivedUpstream(circuit.upstream.buffer, circuit) # XXX touching guts of network.py
+            self.pending_data_to_send = False
 
-        coder = AESCoder(key)
-        coder.encode(iv)
-        return coder
+        circuit.upstream.write(self.recv_crypto.crypt(data.read()))
 
-    def end(self):
+    def _derive_crypto(self, pad_string): # XXX consider secret_seed
         """
-        This is the callback method which is called by the framework when the connection is closed.
-        In Obfs2Daemon it does nothing.
+        Derive and return an obfs2 key using the pad string in 'pad_string'.
         """
+        secret = mac(pad_string, self.initiator_seed + self.responder_seed)
+        return aes.AES_CTR_128(secret[:KEYLEN], secret[KEYLEN:])
 
-        pass
-
+    def _derive_padding_crypto(self, seed, pad_string): # XXX consider secret_seed
+        """
+        Derive and return an obfs2 padding key using the pad string in 'pad_string'.
+        """
+        secret = mac(pad_string, seed)
+        return aes.AES_CTR_128(secret[:KEYLEN], secret[KEYLEN:])
 
 class Obfs2Client(Obfs2Daemon):
 
@@ -263,9 +216,14 @@ class Obfs2Client(Obfs2Daemon):
     The client and server differ in terms of their padding strings.
     """
 
-    def __init__(self, downstreamConnection, upstreamConnection):
-        self.padString = 'Initiator obfuscation padding'
-        self.otherPadString = 'Responder obfuscation padding'
+    def __init__(self):
+        self.send_pad_keytype = 'Initiator obfuscation padding'
+        self.recv_pad_keytype = 'Responder obfuscation padding'
+        self.send_keytype = "Initiator obfuscated data"
+        self.recv_keytype = "Responder obfuscated data"
+        self.we_are_initiator = True
+
+        Obfs2Daemon.__init__(self)
 
 
 class Obfs2Server(Obfs2Daemon):
@@ -275,8 +233,13 @@ class Obfs2Server(Obfs2Daemon):
     The client and server differ in terms of their padding strings.
     """
 
-    def __init__(self, downstreamConnection, upstreamConnection):
-        self.padString = 'Responder obfuscation padding'
-        self.otherPadString = 'Initiator obfuscation padding'
+    def __init__(self):
+        self.send_pad_keytype = 'Responder obfuscation padding'
+        self.recv_pad_keytype = 'Initiator obfuscation padding'
+        self.send_keytype = "Responder obfuscated data"
+        self.recv_keytype = "Initiator obfuscated data"
+        self.we_are_initiator = False
+
+        Obfs2Daemon.__init__(self)
 
 

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



More information about the Pkg-privacy-commits mailing list