diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index 0b0a981..50eb8b3 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -23,7 +23,7 @@ import ( var version string func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) { - // The maximum size of TLS message will be 16396+12. 12 because of the stream header + // The maximum size of TLS message will be 16396+16. 16 because of the stream header // 16408 is the max TLS message size on Firefox buf := make([]byte, 16396) for { diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index ed53139..1bd1f62 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -23,7 +23,7 @@ import ( var version string func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) { - // The maximum size of TLS message will be 16396+12. 12 because of the stream header + // The maximum size of TLS message will be 16396+16. 16 because of the stream header // 16408 is the max TLS message size on Firefox buf := make([]byte, 16396) for { diff --git a/internal/multiplex/obfs.go b/internal/multiplex/obfs.go index 505e255..b68b1de 100644 --- a/internal/multiplex/obfs.go +++ b/internal/multiplex/obfs.go @@ -1,52 +1,46 @@ package multiplex import ( + "crypto/rand" + "crypto/sha1" "encoding/binary" "errors" - - xxhash "github.com/OneOfOne/xxhash" + "io" ) type Obfser func(*Frame) ([]byte, error) type Deobfser func([]byte) (*Frame, error) +var u32 = binary.BigEndian.Uint32 + // For each frame, the three parts of the header is xored with three keys. // The keys are generated from the SID and the payload of the frame. -func genXorKeys(secret []byte, data []byte) (i uint32, ii uint32, iii uint32) { - h := xxhash.New32() - ret := make([]uint32, 3) - preHash := make([]byte, 16) - for j := 0; j < 3; j++ { - copy(preHash[0:10], secret[j*10:j*10+10]) - copy(preHash[10:16], data[j*6:j*6+6]) - h.Write(preHash) - ret[j] = h.Sum32() - } - return ret[0], ret[1], ret[2] +func genXorKeys(key, nonce []byte) (i uint32, ii uint32, iii uint32) { + h := sha1.New() + hashed := h.Sum(append(key, nonce...)) + return u32(hashed[0:4]), u32(hashed[4:8]), u32(hashed[8:12]) } func MakeObfs(key []byte) Obfser { obfs := func(f *Frame) ([]byte, error) { - if len(f.Payload) < 18 { - return nil, errors.New("Payload cannot be shorter than 18 bytes") - } - obfsedHeader := make([]byte, 12) - // header: [StreamID 4 bytes][Seq 4 bytes][Closing 4 bytes] - i, ii, iii := genXorKeys(key, f.Payload[0:18]) + obfsedHeader := make([]byte, 16) + // header: [StreamID 4 bytes][Seq 4 bytes][Closing 4 bytes][Nonce 4 bytes] + io.ReadFull(rand.Reader, obfsedHeader[12:16]) + i, ii, iii := genXorKeys(key, obfsedHeader[12:16]) binary.BigEndian.PutUint32(obfsedHeader[0:4], f.StreamID^i) binary.BigEndian.PutUint32(obfsedHeader[4:8], f.Seq^ii) binary.BigEndian.PutUint32(obfsedHeader[8:12], f.Closing^iii) // Composing final obfsed message // We don't use util.AddRecordLayer here to avoid unnecessary malloc - obfsed := make([]byte, 5+12+len(f.Payload)) + obfsed := make([]byte, 5+16+len(f.Payload)) obfsed[0] = 0x17 obfsed[1] = 0x03 obfsed[2] = 0x03 - binary.BigEndian.PutUint16(obfsed[3:5], uint16(12+len(f.Payload))) - copy(obfsed[5:17], obfsedHeader) - copy(obfsed[17:], f.Payload) - // obfsed: [record layer 5 bytes][cipherheader 12 bytes][payload] + binary.BigEndian.PutUint16(obfsed[3:5], uint16(16+len(f.Payload))) + copy(obfsed[5:21], obfsedHeader) + copy(obfsed[21:], f.Payload) + // obfsed: [record layer 5 bytes][cipherheader 16 bytes][payload] return obfsed, nil } return obfs @@ -54,16 +48,16 @@ func MakeObfs(key []byte) Obfser { func MakeDeobfs(key []byte) Deobfser { deobfs := func(in []byte) (*Frame, error) { - if len(in) < 30 { - return nil, errors.New("Input cannot be shorter than 30 bytes") + if len(in) < 21 { + return nil, errors.New("Input cannot be shorter than 21 bytes") } peeled := in[5:] - i, ii, iii := genXorKeys(key, peeled[12:30]) - streamID := binary.BigEndian.Uint32(peeled[0:4]) ^ i - seq := binary.BigEndian.Uint32(peeled[4:8]) ^ ii - closing := binary.BigEndian.Uint32(peeled[8:12]) ^ iii - payload := make([]byte, len(peeled)-12) - copy(payload, peeled[12:]) + i, ii, iii := genXorKeys(key, peeled[12:16]) + streamID := u32(peeled[0:4]) ^ i + seq := u32(peeled[4:8]) ^ ii + closing := u32(peeled[8:12]) ^ iii + payload := make([]byte, len(peeled)-16) + copy(payload, peeled[16:]) ret := &Frame{ StreamID: streamID, Seq: seq, diff --git a/internal/multiplex/stream.go b/internal/multiplex/stream.go index 84ef6dc..f4d7f6f 100644 --- a/internal/multiplex/stream.go +++ b/internal/multiplex/stream.go @@ -9,7 +9,7 @@ import ( "sync/atomic" ) -var errBrokenStream = errors.New("broken stream") +var ErrBrokenStream = errors.New("broken stream") type Stream struct { id uint32 @@ -18,9 +18,9 @@ type Stream struct { // Explanations of the following 4 fields can be found in frameSorter.go nextRecvSeq uint32 - rev int - sh sorterHeap - wrapMode bool + //rev int + sh sorterHeap + //wrapMode bool // New frames are received through newFrameCh by frameSorter newFrameCh chan *Frame @@ -54,18 +54,18 @@ func (stream *Stream) Read(buf []byte) (n int, err error) { if len(buf) == 0 { select { case <-stream.die: - return 0, errBrokenStream + return 0, ErrBrokenStream default: return 0, nil } } select { case <-stream.die: - return 0, errBrokenStream + return 0, ErrBrokenStream case data := <-stream.sortedBufCh: if len(data) == 0 { stream.passiveClose() - return 0, errBrokenStream + return 0, ErrBrokenStream } if len(buf) < len(data) { log.Println(len(data)) @@ -87,7 +87,7 @@ func (stream *Stream) Write(in []byte) (n int, err error) { select { case <-stream.die: stream.writingM.RUnlock() - return 0, errBrokenStream + return 0, ErrBrokenStream default: } diff --git a/internal/multiplex/switchboard.go b/internal/multiplex/switchboard.go index 4f3bab1..bb85589 100644 --- a/internal/multiplex/switchboard.go +++ b/internal/multiplex/switchboard.go @@ -18,12 +18,6 @@ type switchboard struct { optimum atomic.Value // *connEnclave cesM sync.RWMutex ces []*connEnclave - - /* - //debug - hM sync.Mutex - used map[uint32]bool - */ } func (sb *switchboard) getOptimum() *connEnclave { @@ -34,10 +28,6 @@ func (sb *switchboard) getOptimum() *connEnclave { } } -func (sb *switchboard) setOptimum(ce *connEnclave) { - sb.optimum.Store(ce) -} - // Some data comes from a Stream to be sent through one of the many // remoteConn, but which remoteConn should we use to send the data? // @@ -54,8 +44,6 @@ func makeSwitchboard(sesh *Session, valve *Valve) *switchboard { session: sesh, Valve: valve, ces: []*connEnclave{}, - //debug - // used: make(map[uint32]bool), } return sb } @@ -99,18 +87,19 @@ func (sb *switchboard) updateOptimum() { } } sb.cesM.RUnlock() - sb.setOptimum(currentOpti) + sb.optimum.Store(currentOpti) } func (sb *switchboard) addConn(conn net.Conn) { + var sendQueue uint32 newCe := &connEnclave{ remoteConn: conn, - sendQueue: 0, + sendQueue: sendQueue, } sb.cesM.Lock() sb.ces = append(sb.ces, newCe) sb.cesM.Unlock() - sb.setOptimum(newCe) + sb.optimum.Store(newCe) go sb.deplex(newCe) } @@ -177,14 +166,5 @@ func (sb *switchboard) deplex(ce *connEnclave) { if stream != nil { stream.writeNewFrame(frame) } - //debug - /* - sb.hM.Lock() - if sb.used[frame.StreamID] { - log.Printf("%v lost!\n", frame.StreamID) - } - sb.used[frame.StreamID] = true - sb.hM.Unlock() - */ } }