[Pkg-privacy-commits] [obfsproxy] 101/353: Implement the obfs3 pluggable transport.
Ximin Luo
infinity0 at moszumanska.debian.org
Sat Aug 22 13:01:45 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 86d4f61ea5c3a6a84ae731a16dbe29a9a32bfe01
Author: George Kadianakis <desnacked at riseup.net>
Date: Tue Jan 22 19:51:22 2013 +0200
Implement the obfs3 pluggable transport.
---
obfsproxy/network/network.py | 12 +-
obfsproxy/transports/obfs3.py | 224 +++++++++++++++++++++++++++++++++++++
obfsproxy/transports/transports.py | 4 +-
3 files changed, 236 insertions(+), 4 deletions(-)
diff --git a/obfsproxy/network/network.py b/obfsproxy/network/network.py
index 5d693bf..2116eb5 100644
--- a/obfsproxy/network/network.py
+++ b/obfsproxy/network/network.py
@@ -120,14 +120,20 @@ class Circuit(Protocol):
connected. Do all the things we have to do now.
"""
- # Do a dummy dataReceived on the initiating connection in case
- # it has any buffered data that must be flushed to the network.
- conn_to_flush.dataReceived('') # XXX hack way to flush
+ log.debug("%s: Circuit completed." % self.name)
# Call the transport-specific handshake method since this is a
# good time to perform a handshake.
self.transport.handshake(self)
+ # Do a dummy dataReceived on the initiating connection in case
+ # it has any buffered data that must be flushed to the network.
+ #
+ # (We use callLater because we want to return back to the
+ # event loop so that our handshake() messages get sent to the
+ # network immediately.)
+ reactor.callLater(0.01, conn_to_flush.dataReceived, '')
+
def dataReceived(self, data, conn):
"""
We received 'data' on 'conn'. Pass the data to our transport,
diff --git a/obfsproxy/transports/obfs3.py b/obfsproxy/transports/obfs3.py
new file mode 100644
index 0000000..88390e9
--- /dev/null
+++ b/obfsproxy/transports/obfs3.py
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+The obfs3 module implements the obfs3 protocol.
+"""
+
+import random
+
+import obfsproxy.common.aes as aes
+import obfsproxy.transports.base as base
+import obfsproxy.transports.obfs3_dh as obfs3_dh
+import obfsproxy.common.log as logging
+import obfsproxy.common.hmac_sha256 as hmac_sha256
+import obfsproxy.common.rand as rand
+
+log = logging.get_obfslogger()
+
+MAX_PADDING = 8194
+
+PUBKEY_LEN = 192
+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.
+
+ST_WAIT_FOR_KEY = 0 # Waiting for public key from the other party
+ST_SEARCHING_MAGIC = 1 # Waiting for magic strings from the other party
+ST_OPEN = 2 # obfs3 handshake is complete. Sending application data.
+
+class Obfs3Transport(base.BaseTransport):
+ """
+ Obfs3Transport implements the obfs3 protocol.
+ """
+
+ def __init__(self):
+ """Initialize the obfs3 pluggable transport."""
+
+ # Our state.
+ self.state = ST_WAIT_FOR_KEY
+
+ # Uniform-DH object
+ self.dh = obfs3_dh.UniformDH()
+
+ # DH shared secret
+ self.shared_secret = None
+
+ # Bytes of padding scanned so far.
+ self.scanned_padding = 0
+ # Last padding bytes scanned.
+ self.last_padding_chunk = ''
+
+ # Magic value that the other party is going to send
+ # (initialized after deriving shared secret)
+ self.other_magic_value = None
+ # Crypto to encrypt outgoing data.
+ self.send_crypto = None
+ # Crypto to decrypt incoming data.
+ self.recv_crypto = None
+
+ # Buffer for the first data, Tor is trying to send but can't right now
+ # because we have to handle the DH handshake first.
+ self.queued_data = ''
+
+ # Attributes below are filled by classes that inherit Obfs3Transport.
+ self.send_keytype = None
+ self.recv_keytype = None
+ self.send_magic_const = None
+ self.recv_magic_const = None
+ self.we_are_initiator = None
+
+ def handshake(self, circuit):
+ """
+ Do the obfs3 handshake:
+ PUBKEY | WR(PADLEN)
+ """
+ padding_length = random.randint(0, MAX_PADDING/2)
+
+ handshake_message = self.dh.get_public() + rand.random_bytes(padding_length)
+
+ log.debug("obfs3 handshake: %s queued %d bytes (padding_length: %d) (public key: %s).",
+ "initiator" if self.we_are_initiator else "responder",
+ len(handshake_message), padding_length, repr(self.dh.get_public()))
+
+ circuit.downstream.write(handshake_message)
+
+ def receivedUpstream(self, data, circuit):
+ """
+ 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.queued_data += data.read()
+ return
+
+ message = self.send_crypto.crypt(data.read())
+ log.debug("obfs3 receivedUpstream: Transmitting %d bytes.", len(message))
+
+ # Proxy encrypted message.
+ circuit.downstream.write(message)
+
+ def receivedDownstream(self, data, circuit):
+ """
+ Got data from downstream. We need to de-obfuscate them and
+ proxy them upstream.
+ """
+
+ if self.state == ST_WAIT_FOR_KEY: # Looking for the other peer's pubkey
+ self._read_handshake(data, circuit)
+
+ if self.state == ST_SEARCHING_MAGIC: # Looking for the magic string
+ self._scan_for_magic(data)
+
+ if self.state == ST_OPEN: # Handshake is done. Just decrypt and read application data.
+ log.debug("obfs3 receivedDownstream: Processing %d bytes of application data." %
+ len(data))
+ circuit.upstream.write(self.recv_crypto.crypt(data.read()))
+
+ def _read_handshake(self, data, circuit):
+ """
+ Read handshake message, parse the other peer's public key and
+ set up our crypto.
+ """
+
+ log_prefix = "obfs3:_read_handshake()"
+ if len(data) < PUBKEY_LEN:
+ log.debug("%s: Not enough bytes for key (%d)." % (log_prefix, len(data)))
+ return
+
+ log.debug("%s: Got %d bytes of handshake data (waiting for key)." % (log_prefix, len(data)))
+
+ # Get the public key from the handshake message, do the DH and
+ # get the shared secret.
+ other_pubkey = data.read(PUBKEY_LEN)
+ try:
+ self.shared_secret = self.dh.get_secret(other_pubkey)
+ except ValueError:
+ raise base.PluggableTransportError("obfs3: Corrupted public key '%s'" % repr(other_pubkey))
+ log.debug("Got public key: %s.\nGot shared secret: %s" %
+ (repr(other_pubkey), repr(self.shared_secret)))
+
+ # Set up our crypto.
+ self.send_crypto = self._derive_crypto(self.send_keytype)
+ self.recv_crypto = self._derive_crypto(self.recv_keytype)
+ self.other_magic_value = hmac_sha256.hmac_sha256_digest(self.shared_secret,
+ self.recv_magic_const)
+
+ # Send our magic value to the remote end and append the queued outgoing data.
+ # Padding is prepended so that the server does not just send the 32-byte magic
+ # in a single TCP segment.
+ padding_length = random.randint(0, MAX_PADDING/2)
+ magic = hmac_sha256.hmac_sha256_digest(self.shared_secret, self.send_magic_const)
+ message = rand.random_bytes(padding_length) + magic + self.send_crypto.crypt(self.queued_data)
+ self.queued_data = ''
+
+ log.debug("%s: Transmitting %d bytes (with magic)." % (log_prefix, len(message)))
+ circuit.downstream.write(message)
+
+ self.state = ST_SEARCHING_MAGIC
+
+ def _scan_for_magic(self, data):
+ """
+ Scan 'data' for the magic string. If found, drain it and all
+ the padding before it. Then open the connection.
+ """
+
+ log_prefix = "obfs3:_scan_for_magic()"
+ log.debug("%s: Searching for magic." % log_prefix)
+
+ assert(self.other_magic_value)
+ chunk = data.peek()
+
+ index = chunk.find(self.other_magic_value)
+ if index < 0:
+ if (len(data) > MAX_PADDING):
+ raise base.PluggableTransportError("obfs3: Too much padding (%d)!" % len(data))
+ log.debug("%s: Did not find magic this time (%d)." % (log_prefix, len(data)))
+ return
+
+ index += len(self.other_magic_value)
+ log.debug("%s: Found magic. Draining %d bytes." % (log_prefix, index))
+ data.drain(index)
+
+ self.state = ST_OPEN
+
+ def _derive_crypto(self, pad_string):
+ """
+ Derive and return an obfs3 key using the pad string in 'pad_string'.
+ """
+ secret = hmac_sha256.hmac_sha256_digest(self.shared_secret, pad_string)
+ return aes.AES_CTR_128(secret[:KEYLEN], secret[KEYLEN:])
+
+class Obfs3Client(Obfs3Transport):
+
+ """
+ Obfs3Client is a client for the obfs3 protocol.
+ The client and server differ in terms of their padding strings.
+ """
+
+ def __init__(self):
+ Obfs3Transport.__init__(self)
+
+ self.send_keytype = "Initiator obfuscated data"
+ self.recv_keytype = "Responder obfuscated data"
+ self.send_magic_const = "Initiator magic"
+ self.recv_magic_const = "Responder magic"
+ self.we_are_initiator = True
+
+class Obfs3Server(Obfs3Transport):
+
+ """
+ Obfs3Server is a server for the obfs3 protocol.
+ The client and server differ in terms of their padding strings.
+ """
+
+ def __init__(self):
+ Obfs3Transport.__init__(self)
+
+ self.send_keytype = "Responder obfuscated data"
+ self.recv_keytype = "Initiator obfuscated data"
+ self.send_magic_const = "Responder magic"
+ self.recv_magic_const = "Initiator magic"
+ self.we_are_initiator = False
+
+
+
diff --git a/obfsproxy/transports/transports.py b/obfsproxy/transports/transports.py
index 60fcc52..77aca7c 100644
--- a/obfsproxy/transports/transports.py
+++ b/obfsproxy/transports/transports.py
@@ -2,10 +2,12 @@
import obfsproxy.transports.dummy as dummy
import obfsproxy.transports.b64 as b64
import obfsproxy.transports.obfs2 as obfs2
+import obfsproxy.transports.obfs3 as obfs3
transports = { 'dummy' : {'client' : dummy.DummyClient, 'server' : dummy.DummyServer },
'b64' : {'client' : b64.B64Client, 'server' : b64.B64Server },
- 'obfs2' : {'client' : obfs2.Obfs2Client, 'server' : obfs2.Obfs2Server } }
+ 'obfs2' : {'client' : obfs2.Obfs2Client, 'server' : obfs2.Obfs2Server },
+ 'obfs3' : {'client' : obfs3.Obfs3Client, 'server' : obfs3.Obfs3Server } }
def get_transport_class(name, role):
# Rewrite equivalent roles.
--
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