commit 0806a87f0183b3b74d20fe99184bdfb361d5f443 Author: Philipp Winter phw@torproject.org Date: Mon Mar 10 18:47:15 2014 +0100
Improve packet morphing algorithm.
We only want to run the packet morphing algorithm when we really need it -- which is immediately before we send data. Previously, we would morph immediately upon receiving data which is not optimal.
This should fix https://bugs.torproject.org/10991. Thanks to Yawning Angel who pointed out the problem. --- obfsproxy/transports/scramblesuit/packetmorpher.py | 37 +++++++++++++++++--- obfsproxy/transports/scramblesuit/scramblesuit.py | 31 +++++++--------- 2 files changed, 44 insertions(+), 24 deletions(-)
diff --git a/obfsproxy/transports/scramblesuit/packetmorpher.py b/obfsproxy/transports/scramblesuit/packetmorpher.py index 4a72f0b..081d7dc 100644 --- a/obfsproxy/transports/scramblesuit/packetmorpher.py +++ b/obfsproxy/transports/scramblesuit/packetmorpher.py @@ -8,6 +8,7 @@ morphed network data should then match the probability distribution.
import random
+import message import probdist import const
@@ -39,14 +40,37 @@ class PacketMorpher( object ): self.dist = probdist.new(lambda: random.randint(const.HDR_LENGTH, const.MTU))
+ def getPadding( self, sendCrypter, sendHMAC, dataLen ): + """ + Based on the burst's size, return a ready-to-send padding blurb. + """ + + padLen = self.calcPadding(dataLen) + + assert const.HDR_LENGTH <= padLen < (const.MTU + const.HDR_LENGTH), \ + "Invalid padding length %d." % padLen + + # We have to use two padding messages if the padding is > MTU. + if padLen > const.MTU: + padMsgs = [message.new("", paddingLen=700 - const.HDR_LENGTH), + message.new("", paddingLen=padLen - 700 - \ + const.HDR_LENGTH)] + else: + padMsgs = [message.new("", paddingLen=padLen - const.HDR_LENGTH)] + + blurbs = [msg.encryptAndHMAC(sendCrypter, sendHMAC) for msg in padMsgs] + + return "".join(blurbs) + def calcPadding( self, dataLen ): """ - Based on `dataLen', determines the padding for a network packet. + Based on `dataLen', determine and return a burst's padding.
- ScrambleSuit morphs packets which are smaller than the link's MTU. - This method draws a random sample from the probability distribution - which is used to determine and return the padding for such packets. - This effectively gets rid of Tor's 586-byte signature. + ScrambleSuit morphs the last packet in a burst, i.e., packets which + don't fill the link's MTU. This is done by drawing a random sample + from our probability distribution which is used to determine and return + the padding for such packets. This effectively gets rid of Tor's + 586-byte signature. """
# The `is' and `should-be' length of the burst's last packet. @@ -59,6 +83,9 @@ class PacketMorpher( object ): else: padLen = (const.MTU - dataLen) + sampleLen
+ if padLen < const.HDR_LENGTH: + padLen += const.MTU + log.debug("Morphing the last %d-byte packet to %d bytes by adding %d " "bytes of padding." % (dataLen % const.MTU, sampleLen, padLen)) diff --git a/obfsproxy/transports/scramblesuit/scramblesuit.py b/obfsproxy/transports/scramblesuit/scramblesuit.py index 8e04cc5..43b52b5 100644 --- a/obfsproxy/transports/scramblesuit/scramblesuit.py +++ b/obfsproxy/transports/scramblesuit/scramblesuit.py @@ -234,27 +234,12 @@ class ScrambleSuitTransport( base.BaseTransport ):
# Wrap the application's data in ScrambleSuit protocol messages. messages = message.createProtocolMessages(data, flags=flags) - - # Let the packet morpher tell us how much we should pad. - paddingLen = self.pktMorpher.calcPadding(sum([len(msg) for - msg in messages])) - - # If padding > header length, a single message will do... - if paddingLen > const.HDR_LENGTH: - messages.append(message.new("", paddingLen=paddingLen - - const.HDR_LENGTH)) - - # ...otherwise, we use two padding-only messages. - else: - messages.append(message.new("", paddingLen=const.MPU - - const.HDR_LENGTH)) - messages.append(message.new("", paddingLen=paddingLen)) - blurb = "".join([msg.encryptAndHMAC(self.sendCrypter, self.sendHMAC) for msg in messages])
- # Flush data chunk for chunk to obfuscate inter arrival times. + # Flush data chunk for chunk to obfuscate inter-arrival times. if const.USE_IAT_OBFUSCATION: + if len(self.choppingBuf) == 0: self.choppingBuf.write(blurb) reactor.callLater(self.iatMorpher.randomSample(), @@ -262,8 +247,12 @@ class ScrambleSuitTransport( base.BaseTransport ): else: # flushPieces() is still busy processing the chopping buffer. self.choppingBuf.write(blurb) + else: - self.circuit.downstream.write(blurb) + padBlurb = self.pktMorpher.getPadding(self.sendCrypter, + self.sendHMAC, + len(blurb)) + self.circuit.downstream.write(blurb + padBlurb)
def flushPieces( self ): """ @@ -283,7 +272,11 @@ class ScrambleSuitTransport( base.BaseTransport ):
# Drain and send whatever is left in the output buffer. else: - self.circuit.downstream.write(self.choppingBuf.read()) + blurb = self.choppingBuf.read() + padBlurb = self.pktMorpher.getPadding(self.sendCrypter, + self.sendHMAC, + len(blurb)) + self.circuit.downstream.write(blurb + padBlurb) return
reactor.callLater(self.iatMorpher.randomSample(), self.flushPieces)