From 4d85236d16b0df486e75d3cca1cdf626b746b56d Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Wed, 2 May 2018 12:28:56 -0700 Subject: [PATCH] Chaffing slight improvements (rand size, timing). TODO: Move into hkexsh.Conn --- hkexnet.go | 48 ++++++++++++++++++++++++++++++++++++++++++- hkexsh/hkexsh.go | 53 ++++++++---------------------------------------- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/hkexnet.go b/hkexnet.go index f714ab7..faef277 100644 --- a/hkexnet.go +++ b/hkexnet.go @@ -25,6 +25,7 @@ import ( "math/big" "net" "strings" + "sync" "time" ) @@ -391,7 +392,7 @@ func (c Conn) Read(b []byte) (n int, err error) { // Throw away pkt if it's chaff (ie., caller to Read() won't see this data) if ctrlStatOp == CSOChaff { - log.Printf("[Chaff pkt, discarded]\n") + log.Printf("[Chaff pkt, discarded (len %d)]\n", decryptN) } else if ctrlStatOp == CSOTermSize { fmt.Sscanf(string(payloadBytes), "%d %d", &c.Rows, &c.Cols) log.Printf("[TermSize pkt: rows %v cols %v]\n", c.Rows, c.Cols) @@ -478,3 +479,48 @@ func (c Conn) WritePacket(b []byte, op byte) (n int, err error) { } return } + +// hkexsh.Copy() is a modified version of io.Copy() with locking, +// on a passed-in mutex, around the actual call to Write() to permit +// multiple producers to write hkexsh buffers to the same destination. +// +// (Used to generate chaff during sessions) +func Copy(m *sync.Mutex, dst io.Writer, src io.Reader) (written int64, err error) { + // // If the reader has a WriteTo method, use it to do the copy. + // // Avoids an allocation and a copy. + // if wt, ok := src.(io.WriterTo); ok { + // return wt.WriteTo(dst) + // } + // // Similarly, if the writer has a ReadFrom method, use it to do the copy. + // if rt, ok := dst.(io.ReaderFrom); ok { + // return rt.ReadFrom(src) + // } + + buf := make([]byte, 32*1024) + for { + nr, er := src.Read(buf) + if nr > 0 { + m.Lock() + nw, ew := dst.Write(buf[0:nr]) + m.Unlock() + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = io.ErrShortWrite + break + } + } + if er != nil { + if er != io.EOF { + err = er + } + break + } + } + return written, err +} diff --git a/hkexsh/hkexsh.go b/hkexsh/hkexsh.go index df0ff77..ec423be 100644 --- a/hkexsh/hkexsh.go +++ b/hkexsh/hkexsh.go @@ -13,6 +13,7 @@ import ( "io" "io/ioutil" "log" + "math/rand" "os" "os/exec" "os/signal" @@ -220,15 +221,19 @@ func main() { ch <- syscall.SIGWINCH // Initial resize. // client chaffing goroutine + // TODO: Consider making this a feature of hkexsh.Conn itself wg.Add(1) go func() { defer wg.Done() - for { m.Lock() - conn.WritePacket([]byte("CHAFF"), hkexsh.CSOChaff) + chaff := make([]byte, rand.Intn(512)) + nextDurationMin := 1000 //ms + nextDuration := rand.Intn(5000-nextDurationMin) + nextDurationMin + _, _ = rand.Read(chaff) + conn.WritePacket(chaff, hkexsh.CSOChaff) m.Unlock() - time.Sleep(10 * time.Millisecond) + time.Sleep(time.Duration(nextDuration) * time.Millisecond) } }() @@ -241,7 +246,7 @@ func main() { // exit with outerr == nil //!_, outerr := io.Copy(conn, os.Stdin) _, outerr := func(m *sync.Mutex, conn *hkexsh.Conn, r io.Reader) (w int64, e error) { - return safeCopy(m, conn, r) + return hkexsh.Copy(m, conn, r) }(m, conn, os.Stdin) if outerr != nil { @@ -260,43 +265,3 @@ func main() { // Wait until both stdin and stdout goroutines finish wg.Wait() } - -func safeCopy(m *sync.Mutex, dst io.Writer, src io.Reader) (written int64, err error) { - // // If the reader has a WriteTo method, use it to do the copy. - // // Avoids an allocation and a copy. - // if wt, ok := src.(io.WriterTo); ok { - // return wt.WriteTo(dst) - // } - // // Similarly, if the writer has a ReadFrom method, use it to do the copy. - // if rt, ok := dst.(io.ReaderFrom); ok { - // return rt.ReadFrom(src) - // } - - buf := make([]byte, 32*1024) - for { - nr, er := src.Read(buf) - if nr > 0 { - m.Lock() - nw, ew := dst.Write(buf[0:nr]) - m.Unlock() - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = io.ErrShortWrite - break - } - } - if er != nil { - if er != io.EOF { - err = er - } - break - } - } - return written, err -}