[Pkg-privacy-commits] [obfsproxy] 21/353: Implemented obfs2 and refactored transports to use new API

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 13:01:34 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 3fb18d7613199277c49e8e366549d33fdb992e01
Author: Brandon Wiley <brandon at blanu.net>
Date:   Wed Aug 1 15:52:02 2012 -0500

    Implemented obfs2 and refactored transports to use new API
---
 src/obfsproxy/transports/base.py           |  31 ++++++
 src/obfsproxy/transports/dummy.py          |  17 ++-
 src/obfsproxy/transports/dust_transport.py |  67 ++++++-----
 src/obfsproxy/transports/obfs2.py          | 172 +++++++++++++++++++++++++++++
 src/obfsproxy/transports/obfs3.py          |  67 ++++++-----
 5 files changed, 273 insertions(+), 81 deletions(-)

diff --git a/src/obfsproxy/transports/base.py b/src/obfsproxy/transports/base.py
new file mode 100644
index 0000000..e1feebf
--- /dev/null
+++ b/src/obfsproxy/transports/base.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+class BaseDaemon:
+
+    def __init__(self, decodedSocket, encodedSocket):
+        self.decodedSocket=decodedSocket
+        self.encodedSocket=encodedSocket
+
+    def read(self, socket, data, maxlen):
+        remaining=maxlen-len(data)
+        return data+socket.read(remaining)
+
+    def checkTransition(self, data, maxlen, newState):
+        if len(data)==maxlen:
+            state=newState
+            return True
+        else:
+            return False
+
+    def start(self):
+        pass
+
+    def receivedDecoded(self):
+        pass
+
+    def receivedEncoded(self):
+        pass
+
+    def end(self):
+        pass
diff --git a/src/obfsproxy/transports/dummy.py b/src/obfsproxy/transports/dummy.py
index e1d9e51..f9086b6 100644
--- a/src/obfsproxy/transports/dummy.py
+++ b/src/obfsproxy/transports/dummy.py
@@ -1,20 +1,19 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 
+from obfsproxy.transports.base import BaseDaemon
 
-class DummyDaemon:
-    def __init__(self, client, server)
-        pass
+class DummyDaemon(BaseDaemon):
+    def receivedDecoded(self):
+        data=self.decodedSocket.readAll()
+        self.encodedSocket.write(data)
 
-    def encode(self, data):
-        return data
-
-    def decode(self, data):
-        return data
+    def receivedEncoded(self):
+        data=self.encodedSocket.readAll()
+        self.decodedSocket.write(data)
 
 class DummyClient(DummyDaemon:
     pass
 
-
 class DummyServer(DummyDaemon):
     pass
diff --git a/src/obfsproxy/transports/dust_transport.py b/src/obfsproxy/transports/dust_transport.py
index 9ba511c..cd3a03a 100644
--- a/src/obfsproxy/transports/dust_transport.py
+++ b/src/obfsproxy/transports/dust_transport.py
@@ -9,55 +9,50 @@ STREAM=1
 
 HANDSHAKE_SIZE=IV_SIZE+KEY_SIZE
 
-class DustDaemon:
+class DustDaemon(BaseDaemon):
+
+    def __init__(self, decodedSocket, encodedSocket):
+        BaseDaemon.__init__(self, decodedSocket, encodedSocket)
 
-    def __init__(self, client, server):
-        self.client=client
-        self.server=server
         self.state=HANDSHAKE_WRITE
-        self.encodeBuffer=bytes('')
-        self.decodeBuffer=bytes('')
         self.ekeypair=createEphemeralKeypair()
+        self.epub=bytes('')
 
-    def read(self, data, count):
-        if data:
-            self.decodeBuffer=self.decodeBuffer+data
-        if len(self.decodeBuffer)>=count:
-            data=self.decodeBuffer[:count]
-            self.decodeBuffer=self.decodeBuffer[count:]
-            return data
-        else:
-            return None
+    def start(self):
+        self.encodedSocket.write(self.ekeypair.public.bytes)
 
-    def encode(self, data):
-        if self.state==HANDSHAKE:
-            self.encodeBuffer=self.encodeBuffer+data
-        else:
-            return self.coder.encode(data)
+    def receivedDecoded(self):
+        # If we're in streaming mode, encode and write the incoming data
+        if self.state==STREAM:
+            data=self.decodedSocket.readAll()
+            if data:
+                self.encodedSocket.write(self.coder.encode(data))
+        # Else do nothing, data will buffer until we've done the handshake
 
-    def decode(self, data):
+    def receivedEncoded(self, data):
         if self.state==HANDSHAKE:
-            epub=self.read(data, HANDSHAKE_SIZE)
-            if epub:
-                esession=makeEphemeralSession(self.ekeypair, epub)
+            self.epub=self.read(self.encodedSocket, self.epub, HANDSHAKE_SIZE)
+            if self.checkTransition(self.epub, HANDSHAKE_SIZE, STREAM):
+                esession=makeEphemeralSession(self.ekeypair, self.epub)
                 self.coder=lite_socket(esession)
-                self.state=STREAM
-                self.server.write(self.encode(self.encodeBuffer))
-                return decode(self.decodeBuffer)
-            else:
-                return None
+
+                data=self.decodedSocket.readAll()
+                if data:
+                    self.encodedSocket.write(self.coder.encode(data))
+
+                data=self.encodedSocket.readAll()
+                if data:
+                    self.decodedSocket.write(self.coder.decode(data))
         else:
-            return self.coder.decode(data)
+            data=self.encodedSocket.readAll()
+            if data:
+                self.decodedSocket.write(self.coder.decode(data))
 
     def end(self):
         pass
 
 class DustClient(DustDaemon):
-
-    def start(self):
-        self.server.write(self.ekeypair.public.bytes)
+    pass
 
 class DustServer(DustDaemon):
-
-    def start(self):
-        self.client.write(ekeypair.public.bytes)
+    pass
diff --git a/src/obfsproxy/transports/obfs2.py b/src/obfsproxy/transports/obfs2.py
new file mode 100644
index 0000000..25307be
--- /dev/null
+++ b/src/obfsproxy/transports/obfs2.py
@@ -0,0 +1,172 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import os
+
+import random
+import hmac
+import hashlib
+import base64
+
+from obfsproxy.crypto.aes import AESCoder
+from obfsproxy.util import decode, uncompact
+
+MAGIC_VALUE      = decode('2BF5CA7E')
+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
+
+# H(x) is SHA256 of x.
+def h(x):
+    hasher = hashlib.sha256()
+    hasher.update(x)
+    return hasher.digest()
+
+# H^n(x) is H(x) called iteratively n times.
+def hn(x, n):
+    data=x
+    for x in range(n):
+        data=h(data)
+    return data
+
+# E(k,s) is the AES-CTR-128 encryption of s using K as key.
+def e(k, s):
+    cipher=AESCoder(k)
+    return cipher.encode(s)
+
+# D(k, s) is the AES-CTR-128 decryption of s using K as key.
+def d(k, s):
+    cipher=AESCoder(k)
+    return cipher.decode(s)
+
+# UINT32(n) is the 4 byte value of n in big-endian (network) order.
+def uint32(n):
+    return struct.pack('!I', (n))
+
+def decodeUint32(bs):
+    return struct.unpack('!I', bs)[0]
+
+# SR(n) is n bytes of strong random data.
+def sr(n):
+    return os.urandom(n)
+
+# WR(n) is n bytes of weaker random data.
+def wr(n):
+    return ''.join(chr(random.randint(0,255)) for _ in range(n))
+
+# MAC(s, x) = H(s | x | s)
+def mac(s, x):
+    return h(s+x+s)
+
+class Obfs2Daemon(BaseDaemon):
+
+    def __init__(self, decodedSocket, encodedSocket):
+        self.decodedSocket=decodedSocket
+        self.encodedSocket=encodedSocket
+        self.otherSeed=bytes('')
+        self.otherPadKeyEncrypted=bytes('')
+        self.otherPadLenBytes=bytes('')
+
+    def start(self):
+        # The initiator generates:
+        # INIT_SEED = SR(SEED_LENGTH)
+
+        self.seed=sr(SEED_LENGTH)
+        self.padKey=self.derivePadKey(self.seed, self.padString)
+
+        # Each then generates a random number PADLEN in range from 0 through MAX_PADDING (inclusive).
+        self.padLen=random.nextInt(MAX_PADDING) % MAX_PADDING
+
+        # The initiator then sends:
+        # INIT_SEED | E(INIT_PAD_KEY, UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN))
+        self.server.write(self.seed+e(self.padKey, uint32(MAGIC_VALUE))+uint32(self.padLen)+wr(self.padLen))
+
+        self.state=READ_SEED
+
+    def receivedDecoded(self):
+        if state==STREAM:
+            data=self.decodedSocket.readAll()
+            encodedData=encode(data)
+            self.encodedSocket.write(encodedData)
+
+    def receivedEncoded(self):
+        if state==READ_SEED:
+            self.otherSeed=self.read(self.encodedSocket, 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.encodedSocket, self.otherPadKeyEncrypted, KEYLEN)
+            if self.checkTransition(self.otherPadKeyEncrypted, KEYLEN, READ_PADLEN):
+                if self.otherPadKeyEncrypted!=self.otherPadKeyDerived:
+                    self.encodedSocket.disconnect()
+                    self.decodedSocket.disconnect()
+        elif state==READ_PADLEN:
+            self.otherPadLenBytes=self.read(self.encodedSocket, self.otherPadLenBytes, 4)
+            if self.checkTransition(self.otherPadLenBytes, 4, READ_PADDING):
+                self.otherPadLen=decodeUint32(self.otherPadLenBytes)
+                if self.otherPadLen>MAX_PADDING:
+                    self.encodedSocket.disconnect()
+                    self.decodedSocket.disconnect()
+        elif state==READ_PADDING:
+            self.otherPadding=self.read(self.encodedSocket, 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.encodedSocket.readAll()
+            decodedData=decode(data)
+            self.decodedSocket.write(decodedData)
+
+    def derivePadKey(self, seed, padString):
+        return mac(padString, seed)[:KEYLEN]
+
+    def deriveSecret(self, seed, otherSeed, server):
+        if server:
+            # RESP_SECRET = MAC("Responder obfuscated data", INIT_SEED|RESP_SEED)
+            return mac(self.padString, otherSeed+seed)
+        else:
+            # INIT_SECRET = MAC("Initiator obfuscated data", INIT_SEED|RESP_SEED)
+            return mac(self.padString, seed+otherSeed)
+
+    def initCipher(self, iv, key):
+        coder=AESCoder(key)
+        coder.encode(iv)
+        return coder
+
+    def end(self):
+        pass
+
+class Obfs2Client(Obfs3Daemon):
+
+    def __init__(self, decodedSocket, encodedSocket):
+        self.padString='Initiator obfuscation padding'
+        self.otherPadString='Responder obfuscation padding'
+
+class Obfs2Server(Obfs3Daemon):
+
+    def __init__(self, decodedSocket, encodedSocket):
+        self.padString='Responder obfuscation padding'
+        self.otherPadString='Initiator obfuscation padding'
diff --git a/src/obfsproxy/transports/obfs3.py b/src/obfsproxy/transports/obfs3.py
index 0b314e0..c4b4643 100644
--- a/src/obfsproxy/transports/obfs3.py
+++ b/src/obfsproxy/transports/obfs3.py
@@ -11,55 +11,50 @@ STREAM=1
 
 HANDSHAKE_SIZE=IV_SIZE+KEY_SIZE
 
-class Obfs3Daemon:
+class Obfs3Daemon(BaseDaemon):
+
+    def __init__(self, decodedSocket, encodedSocket):
+        BaseDaemon.__init__(self, decodedSocket, encodedSocket)
 
-    def __init__(self, client, server):
-        self.client=client
-        self.server=server
         self.state=HANDSHAKE_WRITE
-        self.encodeBuffer=bytes('')
-        self.decodeBuffer=bytes('')
         self.ekeypair=createEphemeralKeypair()
+        self.epub=bytes('')
 
-    def read(self, data, count):
-        if data:
-            self.decodeBuffer=self.decodeBuffer+data
-        if len(self.decodeBuffer)>=count:
-            data=self.decodeBuffer[:count]
-            self.decodeBuffer=self.decodeBuffer[count:]
-            return data
-        else:
-            return None
+    def start(self):
+        self.encodedSocket.write(self.ekeypair.public.bytes)
 
-    def encode(self, data):
-        if self.state==HANDSHAKE:
-            self.encodeBuffer=self.encodeBuffer+data
-        else:
-            return self.coder.encode(data)
+    def receivedDecoded(self):
+        # If we're in streaming mode, encode and write the incoming data
+        if self.state==STREAM:
+            data=self.decodedSocket.readAll()
+            if data:
+                self.encodedSocket.write(self.coder.encode(data))
+        # Else do nothing, data will buffer until we've done the handshake
 
-    def decode(self, data):
+    def receivedEncoded(self, data):
         if self.state==HANDSHAKE:
-            epub=self.read(data, HANDSHAKE_SIZE)
-            if epub:
-                esession=makeEphemeralSession(self.ekeypair, epub)
+            self.epub=self.read(self.encodedSocket, self.epub, HANDSHAKE_SIZE)
+            if self.checkTransition(self.epub, HANDSHAKE_SIZE, STREAM):
+                esession=makeEphemeralSession(self.ekeypair, self.epub)
                 self.coder=AESCoder(esession)
-                self.state=STREAM
-                self.server.write(self.encode(self.encodeBuffer))
-                return decode(self.decodeBuffer)
-            else:
-                return None
+
+                data=self.decodedSocket.readAll()
+                if data:
+                    self.encodedSocket.write(self.coder.encode(data))
+
+                data=self.encodedSocket.readAll()
+                if data:
+                    self.decodedSocket.write(self.coder.decode(data))
         else:
-            return self.coder.decode(data)
+            data=self.encodedSocket.readAll()
+            if data:
+                self.decodedSocket.write(self.coder.decode(data))
 
     def end(self):
         pass
 
 class Obfs3Client(Obfs3Daemon):
-
-    def start(self):
-        self.server.write(self.ekeypair.public.bytes)
+    pass
 
 class Obfs3Server(Obfs3Daemon):
-
-    def start(self):
-        self.client.write(ekeypair.public.bytes)
+    pass

-- 
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