[Pkg-privacy-commits] [obfs4proxy] 58/151: Randomize length when the decoder receives an out-of-bound value.

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 12:59:38 UTC 2015


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

infinity0 pushed a commit to branch master
in repository obfs4proxy.

commit 8cd2e1f0f9335221fbad892efdbb5e02e787a1e8
Author: Yawning Angel <yawning at schwanenlied.me>
Date:   Sat May 24 05:06:34 2014 +0000

    Randomize length when the decoder receives an out-of-bound value.
    
    This makes the length error and MAC error indistinguishable to an
    external attacker.
---
 framing/framing.go      | 39 ++++++++++++++++++++++++---------------
 framing/framing_test.go |  2 +-
 2 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/framing/framing.go b/framing/framing.go
index bbb41cc..b51a7af 100644
--- a/framing/framing.go
+++ b/framing/framing.go
@@ -65,6 +65,8 @@ import (
 	"code.google.com/p/go.crypto/nacl/secretbox"
 
 	"github.com/dchest/siphash"
+
+	"github.com/yawning/obfs4/csrand"
 )
 
 const (
@@ -111,14 +113,6 @@ func (e InvalidPayloadLengthError) Error() string {
 	return fmt.Sprintf("framing: Invalid payload length: %d", int(e))
 }
 
-// InvalidFrameLengthError is the error returned when Decoder.Decode()
-// rejects the payload length.
-type InvalidFrameLengthError int
-
-func (e InvalidFrameLengthError) Error() string {
-	return fmt.Sprintf("framing: Invalid frame length: %d", int(e))
-}
-
 type boxNonce struct {
 	prefix  [noncePrefixLength]byte
 	counter uint64
@@ -179,7 +173,7 @@ func (encoder *Encoder) Encode(frame, payload []byte) (n int, err error) {
 	if MaximumFramePayloadLength < payloadLen {
 		return 0, InvalidPayloadLengthError(payloadLen)
 	}
-	if len(frame) < payloadLen + FrameOverhead {
+	if len(frame) < payloadLen+FrameOverhead {
 		return 0, io.ErrShortBuffer
 	}
 
@@ -195,7 +189,7 @@ func (encoder *Encoder) Encode(frame, payload []byte) (n int, err error) {
 	box := secretbox.Seal(frame[:lengthLength], payload, &nonce, &encoder.key)
 
 	// Obfuscate the length.
-	length := uint16(len(box)-lengthLength)
+	length := uint16(len(box) - lengthLength)
 	encoder.sip.Write(nonce[:])
 	lengthMask := encoder.sip.Sum(nil)
 	encoder.sip.Reset()
@@ -212,8 +206,9 @@ type Decoder struct {
 	nonce boxNonce
 	sip   hash.Hash64
 
-	nextNonce  [nonceLength]byte
-	nextLength uint16
+	nextNonce         [nonceLength]byte
+	nextLength        uint16
+	nextLengthInvalid bool
 }
 
 // NewDecoder creates a new Decoder instance.  It must be supplied a slice
@@ -266,7 +261,19 @@ func (decoder *Decoder) Decode(data []byte, frames *bytes.Buffer) (int, error) {
 		decoder.sip.Reset()
 		length ^= binary.BigEndian.Uint16(lengthMask)
 		if maxFrameLength < length || minFrameLength > length {
-			return 0, InvalidFrameLengthError(length)
+			// Per "Plaintext Recovery Attacks Against SSH" by
+			// Martin R. Albrecht, Kenneth G. Paterson and Gaven J. Watson,
+			// there are a class of attacks againt protocols that use similar
+			// sorts of framing schemes.
+			//
+			// While obfs4 should not allow plaintext recovery (CBC mode is
+			// not used), attempt to mitigate out of bound frame length errors
+			// by pretending that the length was a random valid range as per
+			// the countermeasure suggested by Denis Bider in section 6 of the
+			// paper.
+
+			decoder.nextLengthInvalid = true
+			length = uint16(csrand.IntRange(minFrameLength, maxFrameLength))
 		}
 		decoder.nextLength = length
 	}
@@ -283,10 +290,12 @@ func (decoder *Decoder) Decode(data []byte, frames *bytes.Buffer) (int, error) {
 	} else if n != int(decoder.nextLength) {
 		// Should *NEVER* happen, since the length is checked.
 		panic(fmt.Sprintf("BUG: Failed to read secretbox, got %d, should have %d",
-						  n, decoder.nextLength))
+			n, decoder.nextLength))
 	}
 	out, ok := secretbox.Open(data[:0], box[:n], &decoder.nextNonce, &decoder.key)
-	if !ok {
+	if !ok || decoder.nextLengthInvalid {
+		// When a random lenght is used (on length error) the tag should always
+		// mismatch, but be paranoid.
 		return 0, ErrTagMismatch
 	}
 
diff --git a/framing/framing_test.go b/framing/framing_test.go
index 08f5f17..7df0e28 100644
--- a/framing/framing_test.go
+++ b/framing/framing_test.go
@@ -86,7 +86,7 @@ func TestEncoder_Encode_Oversize(t *testing.T) {
 	encoder := newEncoder(t)
 
 	var frame [MaximumSegmentLength]byte
-	var buf [MaximumFramePayloadLength+1]byte
+	var buf [MaximumFramePayloadLength + 1]byte
 	_, _ = rand.Read(buf[:]) // YOLO
 	_, err := encoder.Encode(frame[:], buf[:])
 	if _, ok := err.(InvalidPayloadLengthError); !ok {

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



More information about the Pkg-privacy-commits mailing list