From 8a7de7e1e252a73d786bafab042eedde8c025ad6 Mon Sep 17 00:00:00 2001 From: Brandon Wiley Date: Tue, 15 Nov 2016 14:52:04 -0600 Subject: Removed transports from shapeshifter-dispatcher (now located in shapeshifter-transports) --- transports/obfs4/framing/framing.go | 306 ------------------------------- transports/obfs4/framing/framing_test.go | 169 ----------------- 2 files changed, 475 deletions(-) delete mode 100644 transports/obfs4/framing/framing.go delete mode 100644 transports/obfs4/framing/framing_test.go (limited to 'transports/obfs4/framing') diff --git a/transports/obfs4/framing/framing.go b/transports/obfs4/framing/framing.go deleted file mode 100644 index 9eaba69..0000000 --- a/transports/obfs4/framing/framing.go +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2014, Yawning Angel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -// -// Package framing implements the obfs4 link framing and cryptography. -// -// The Encoder/Decoder shared secret format is: -// uint8_t[32] NaCl secretbox key -// uint8_t[16] NaCl Nonce prefix -// uint8_t[16] SipHash-2-4 key (used to obfsucate length) -// uint8_t[8] SipHash-2-4 IV -// -// The frame format is: -// uint16_t length (obfsucated, big endian) -// NaCl secretbox (Poly1305/XSalsa20) containing: -// uint8_t[16] tag (Part of the secretbox construct) -// uint8_t[] payload -// -// The length field is length of the NaCl secretbox XORed with the truncated -// SipHash-2-4 digest ran in OFB mode. -// -// Initialize K, IV[0] with values from the shared secret. -// On each packet, IV[n] = H(K, IV[n - 1]) -// mask[n] = IV[n][0:2] -// obfsLen = length ^ mask[n] -// -// The NaCl secretbox (Poly1305/XSalsa20) nonce format is: -// uint8_t[24] prefix (Fixed) -// uint64_t counter (Big endian) -// -// The counter is initialized to 1, and is incremented on each frame. Since -// the protocol is designed to be used over a reliable medium, the nonce is not -// transmitted over the wire as both sides of the conversation know the prefix -// and the initial counter value. It is imperative that the counter does not -// wrap, and sessions MUST terminate before 2^64 frames are sent. -// -package framing - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - - "golang.org/x/crypto/nacl/secretbox" - - "github.com/OperatorFoundation/obfs4/common/csrand" - "github.com/OperatorFoundation/obfs4/common/drbg" -) - -const ( - // MaximumSegmentLength is the length of the largest possible segment - // including overhead. - MaximumSegmentLength = 1500 - (40 + 12) - - // FrameOverhead is the length of the framing overhead. - FrameOverhead = lengthLength + secretbox.Overhead - - // MaximumFramePayloadLength is the length of the maximum allowed payload - // per frame. - MaximumFramePayloadLength = MaximumSegmentLength - FrameOverhead - - // KeyLength is the length of the Encoder/Decoder secret key. - KeyLength = keyLength + noncePrefixLength + drbg.SeedLength - - maxFrameLength = MaximumSegmentLength - lengthLength - minFrameLength = FrameOverhead - lengthLength - - keyLength = 32 - - noncePrefixLength = 16 - nonceCounterLength = 8 - nonceLength = noncePrefixLength + nonceCounterLength - - lengthLength = 2 -) - -// Error returned when Decoder.Decode() requires more data to continue. -var ErrAgain = errors.New("framing: More data needed to decode") - -// Error returned when Decoder.Decode() failes to authenticate a frame. -var ErrTagMismatch = errors.New("framing: Poly1305 tag mismatch") - -// Error returned when the NaCl secretbox nonce's counter wraps (FATAL). -var ErrNonceCounterWrapped = errors.New("framing: Nonce counter wrapped") - -// InvalidPayloadLengthError is the error returned when Encoder.Encode() -// rejects the payload length. -type InvalidPayloadLengthError int - -func (e InvalidPayloadLengthError) Error() string { - return fmt.Sprintf("framing: Invalid payload length: %d", int(e)) -} - -type boxNonce struct { - prefix [noncePrefixLength]byte - counter uint64 -} - -func (nonce *boxNonce) init(prefix []byte) { - if noncePrefixLength != len(prefix) { - panic(fmt.Sprintf("BUG: Nonce prefix length invalid: %d", len(prefix))) - } - - copy(nonce.prefix[:], prefix) - nonce.counter = 1 -} - -func (nonce boxNonce) bytes(out *[nonceLength]byte) error { - // The security guarantee of Poly1305 is broken if a nonce is ever reused - // for a given key. Detect this by checking for counter wraparound since - // we start each counter at 1. If it ever happens that more than 2^64 - 1 - // frames are transmitted over a given connection, support for rekeying - // will be neccecary, but that's unlikely to happen. - if nonce.counter == 0 { - return ErrNonceCounterWrapped - } - - copy(out[:], nonce.prefix[:]) - binary.BigEndian.PutUint64(out[noncePrefixLength:], nonce.counter) - - return nil -} - -// Encoder is a frame encoder instance. -type Encoder struct { - key [keyLength]byte - nonce boxNonce - drbg *drbg.HashDrbg -} - -// NewEncoder creates a new Encoder instance. It must be supplied a slice -// containing exactly KeyLength bytes of keying material. -func NewEncoder(key []byte) *Encoder { - if len(key) != KeyLength { - panic(fmt.Sprintf("BUG: Invalid encoder key length: %d", len(key))) - } - - encoder := new(Encoder) - copy(encoder.key[:], key[0:keyLength]) - encoder.nonce.init(key[keyLength : keyLength+noncePrefixLength]) - seed, err := drbg.SeedFromBytes(key[keyLength+noncePrefixLength:]) - if err != nil { - panic(fmt.Sprintf("BUG: Failed to initialize DRBG: %s", err)) - } - encoder.drbg, _ = drbg.NewHashDrbg(seed) - - return encoder -} - -// Encode encodes a single frame worth of payload and returns the encoded -// length. InvalidPayloadLengthError is recoverable, all other errors MUST be -// treated as fatal and the session aborted. -func (encoder *Encoder) Encode(frame, payload []byte) (n int, err error) { - payloadLen := len(payload) - if MaximumFramePayloadLength < payloadLen { - return 0, InvalidPayloadLengthError(payloadLen) - } - if len(frame) < payloadLen+FrameOverhead { - return 0, io.ErrShortBuffer - } - - // Generate a new nonce. - var nonce [nonceLength]byte - if err = encoder.nonce.bytes(&nonce); err != nil { - return 0, err - } - encoder.nonce.counter++ - - // Encrypt and MAC payload. - box := secretbox.Seal(frame[:lengthLength], payload, &nonce, &encoder.key) - - // Obfuscate the length. - length := uint16(len(box) - lengthLength) - lengthMask := encoder.drbg.NextBlock() - length ^= binary.BigEndian.Uint16(lengthMask) - binary.BigEndian.PutUint16(frame[:2], length) - - // Return the frame. - return len(box), nil -} - -// Decoder is a frame decoder instance. -type Decoder struct { - key [keyLength]byte - nonce boxNonce - drbg *drbg.HashDrbg - - nextNonce [nonceLength]byte - nextLength uint16 - nextLengthInvalid bool -} - -// NewDecoder creates a new Decoder instance. It must be supplied a slice -// containing exactly KeyLength bytes of keying material. -func NewDecoder(key []byte) *Decoder { - if len(key) != KeyLength { - panic(fmt.Sprintf("BUG: Invalid decoder key length: %d", len(key))) - } - - decoder := new(Decoder) - copy(decoder.key[:], key[0:keyLength]) - decoder.nonce.init(key[keyLength : keyLength+noncePrefixLength]) - seed, err := drbg.SeedFromBytes(key[keyLength+noncePrefixLength:]) - if err != nil { - panic(fmt.Sprintf("BUG: Failed to initialize DRBG: %s", err)) - } - decoder.drbg, _ = drbg.NewHashDrbg(seed) - - return decoder -} - -// Decode decodes a stream of data and returns the length if any. ErrAgain is -// a temporary failure, all other errors MUST be treated as fatal and the -// session aborted. -func (decoder *Decoder) Decode(data []byte, frames *bytes.Buffer) (int, error) { - // A length of 0 indicates that we do not know how big the next frame is - // going to be. - if decoder.nextLength == 0 { - // Attempt to pull out the next frame length. - if lengthLength > frames.Len() { - return 0, ErrAgain - } - - // Remove the length field from the buffer. - var obfsLen [lengthLength]byte - _, err := io.ReadFull(frames, obfsLen[:]) - if err != nil { - return 0, err - } - - // Derive the nonce the peer used. - if err = decoder.nonce.bytes(&decoder.nextNonce); err != nil { - return 0, err - } - - // Deobfuscate the length field. - length := binary.BigEndian.Uint16(obfsLen[:]) - lengthMask := decoder.drbg.NextBlock() - length ^= binary.BigEndian.Uint16(lengthMask) - if maxFrameLength < length || minFrameLength > 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 - } - - if int(decoder.nextLength) > frames.Len() { - return 0, ErrAgain - } - - // Unseal the frame. - var box [maxFrameLength]byte - n, err := io.ReadFull(frames, box[:decoder.nextLength]) - if err != nil { - return 0, err - } - out, ok := secretbox.Open(data[:0], box[:n], &decoder.nextNonce, &decoder.key) - if !ok || decoder.nextLengthInvalid { - // When a random length is used (on length error) the tag should always - // mismatch, but be paranoid. - return 0, ErrTagMismatch - } - - // Clean up and prepare for the next frame. - decoder.nextLength = 0 - decoder.nonce.counter++ - - return len(out), nil -} diff --git a/transports/obfs4/framing/framing_test.go b/transports/obfs4/framing/framing_test.go deleted file mode 100644 index 03e0d3b..0000000 --- a/transports/obfs4/framing/framing_test.go +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2014, Yawning Angel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package framing - -import ( - "bytes" - "crypto/rand" - "testing" -) - -func generateRandomKey() []byte { - key := make([]byte, KeyLength) - - _, err := rand.Read(key) - if err != nil { - panic(err) - } - - return key -} - -func newEncoder(t *testing.T) *Encoder { - // Generate a key to use. - key := generateRandomKey() - - encoder := NewEncoder(key) - if encoder == nil { - t.Fatalf("NewEncoder returned nil") - } - - return encoder -} - -// TestNewEncoder tests the Encoder ctor. -func TestNewEncoder(t *testing.T) { - encoder := newEncoder(t) - _ = encoder -} - -// TestEncoder_Encode tests Encoder.Encode. -func TestEncoder_Encode(t *testing.T) { - encoder := newEncoder(t) - - buf := make([]byte, MaximumFramePayloadLength) - _, _ = rand.Read(buf) // YOLO - for i := 0; i <= MaximumFramePayloadLength; i++ { - var frame [MaximumSegmentLength]byte - n, err := encoder.Encode(frame[:], buf[0:i]) - if err != nil { - t.Fatalf("Encoder.encode([%d]byte), failed: %s", i, err) - } - if n != i+FrameOverhead { - t.Fatalf("Unexpected encoded framesize: %d, expecting %d", n, i+ - FrameOverhead) - } - } -} - -// TestEncoder_Encode_Oversize tests oversized frame rejection. -func TestEncoder_Encode_Oversize(t *testing.T) { - encoder := newEncoder(t) - - var frame [MaximumSegmentLength]byte - var buf [MaximumFramePayloadLength + 1]byte - _, _ = rand.Read(buf[:]) // YOLO - _, err := encoder.Encode(frame[:], buf[:]) - if _, ok := err.(InvalidPayloadLengthError); !ok { - t.Error("Encoder.encode() returned unexpected error:", err) - } -} - -// TestNewDecoder tests the Decoder ctor. -func TestNewDecoder(t *testing.T) { - key := generateRandomKey() - decoder := NewDecoder(key) - if decoder == nil { - t.Fatalf("NewDecoder returned nil") - } -} - -// TestDecoder_Decode tests Decoder.Decode. -func TestDecoder_Decode(t *testing.T) { - key := generateRandomKey() - - encoder := NewEncoder(key) - decoder := NewDecoder(key) - - var buf [MaximumFramePayloadLength]byte - _, _ = rand.Read(buf[:]) // YOLO - for i := 0; i <= MaximumFramePayloadLength; i++ { - var frame [MaximumSegmentLength]byte - encLen, err := encoder.Encode(frame[:], buf[0:i]) - if err != nil { - t.Fatalf("Encoder.encode([%d]byte), failed: %s", i, err) - } - if encLen != i+FrameOverhead { - t.Fatalf("Unexpected encoded framesize: %d, expecting %d", encLen, - i+FrameOverhead) - } - - var decoded [MaximumFramePayloadLength]byte - - decLen, err := decoder.Decode(decoded[:], bytes.NewBuffer(frame[:encLen])) - if err != nil { - t.Fatalf("Decoder.decode([%d]byte), failed: %s", i, err) - } - if decLen != i { - t.Fatalf("Unexpected decoded framesize: %d, expecting %d", - decLen, i) - } - - if 0 != bytes.Compare(decoded[:decLen], buf[:i]) { - t.Fatalf("Frame %d does not match encoder input", i) - } - } -} - -// BencharkEncoder_Encode benchmarks Encoder.Encode processing 1 MiB -// of payload. -func BenchmarkEncoder_Encode(b *testing.B) { - var chopBuf [MaximumFramePayloadLength]byte - var frame [MaximumSegmentLength]byte - payload := make([]byte, 1024*1024) - encoder := NewEncoder(generateRandomKey()) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - transfered := 0 - buffer := bytes.NewBuffer(payload) - for 0 < buffer.Len() { - n, err := buffer.Read(chopBuf[:]) - if err != nil { - b.Fatal("buffer.Read() failed:", err) - } - - n, err = encoder.Encode(frame[:], chopBuf[:n]) - transfered += n - FrameOverhead - } - if transfered != len(payload) { - b.Fatalf("Transfered length mismatch: %d != %d", transfered, - len(payload)) - } - } -} -- cgit v1.2.3