De-packetizing-rebuffering of Read() w/HMAC working, yay

This commit is contained in:
Russ Magee 2018-03-25 19:58:04 -07:00
parent c0fa2bcdf9
commit 3f5db06f16
3 changed files with 82 additions and 64 deletions

View File

@ -129,8 +129,6 @@ func main() {
_, err = conn.Write(rec.cmd) _, err = conn.Write(rec.cmd)
_, err = conn.Write(rec.authCookie) _, err = conn.Write(rec.authCookie)
conn.EnableHMAC()
//client reader (from server) goroutine //client reader (from server) goroutine
wg.Add(1) wg.Add(1)
go func() { go func() {
@ -153,6 +151,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
} }
if isInteractive { if isInteractive {
log.Println("[Got EOF]") log.Println("[Got EOF]")
wg.Done() // server hung up, close WaitGroup to exit client wg.Done() // server hung up, close WaitGroup to exit client

View File

@ -178,6 +178,8 @@ func main() {
var len1, len2, len3, len4 uint32 var len1, len2, len3, len4 uint32
n, err := fmt.Fscanf(c, "%d %d %d %d\n", &len1, &len2, &len3, &len4) n, err := fmt.Fscanf(c, "%d %d %d %d\n", &len1, &len2, &len3, &len4)
log.Printf("cmdSpec read:%d %d %d %d\n", len1, len2, len3, len4)
if err != nil || n < 4 { if err != nil || n < 4 {
log.Println("[Bad cmdSpec fmt]") log.Println("[Bad cmdSpec fmt]")
return err return err
@ -211,8 +213,6 @@ func main() {
return err return err
} }
conn.EnableHMAC()
log.Printf("[cmdSpec: op:%c who:%s cmd:%s auth:****]\n", log.Printf("[cmdSpec: op:%c who:%s cmd:%s auth:****]\n",
rec.op[0], string(rec.who), string(rec.cmd)) rec.op[0], string(rec.who), string(rec.cmd))

View File

@ -24,9 +24,11 @@ package herradurakex
import ( import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"encoding/binary"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"hash" "hash"
"io"
"log" "log"
"math/big" "math/big"
"net" "net"
@ -40,17 +42,13 @@ import (
type Conn struct { type Conn struct {
c net.Conn // which also implements io.Reader, io.Writer, ... c net.Conn // which also implements io.Reader, io.Writer, ...
h *HerraduraKEx h *HerraduraKEx
hmacOn bool // turned on once channel param negotiation is done
cipheropts uint32 // post-KEx cipher/hmac options cipheropts uint32 // post-KEx cipher/hmac options
opts uint32 // post-KEx protocol options (caller-defined) opts uint32 // post-KEx protocol options (caller-defined)
r cipher.Stream //read cipherStream r cipher.Stream //read cipherStream
rm hash.Hash rm hash.Hash
w cipher.Stream //write cipherStream w cipher.Stream //write cipherStream
wm hash.Hash wm hash.Hash
} dBuf *bytes.Buffer //decrypt buffer for Read()
func (c *Conn) EnableHMAC() {
c.hmacOn = true
} }
// ConnOpts returns the cipher/hmac options value, which is sent to the // ConnOpts returns the cipher/hmac options value, which is sent to the
@ -134,7 +132,7 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
return nil, err return nil, err
} }
// Init hkexnet.Conn hc over net.Conn c // Init hkexnet.Conn hc over net.Conn c
hc = &Conn{c: c, h: New(0, 0)} hc = &Conn{c: c, h: New(0, 0), dBuf: new(bytes.Buffer)}
hc.applyConnExtensions(extensions...) hc.applyConnExtensions(extensions...)
// Send hkexnet.Conn parameters to remote side // Send hkexnet.Conn parameters to remote side
@ -266,7 +264,7 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
} }
log.Println("[Accepted]") log.Println("[Accepted]")
hc = Conn{c: c, h: New(0, 0)} hc = Conn{c: c, h: New(0, 0), dBuf: new(bytes.Buffer)}
// Read in hkexnet.Conn parameters over raw Conn c // Read in hkexnet.Conn parameters over raw Conn c
// d is value for Herradura key exchange // d is value for Herradura key exchange
@ -301,16 +299,33 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
// See go doc io.Reader // See go doc io.Reader
func (c Conn) Read(b []byte) (n int, err error) { func (c Conn) Read(b []byte) (n int, err error) {
//log.Printf("[Decrypting...]\r\n") //log.Printf("[Decrypting...]\r\n")
var hIn []byte = make([]byte, 1, 1) log.Printf("Read() requests %d bytes\n", len(b))
for {
if c.hmacOn { log.Printf("c.dBuf.Len(): %d\n", c.dBuf.Len())
_ = hIn if c.dBuf.Len() >= 1 /* len(b) */ {
//_, _ = io.ReadFull(c.c, hIn) break
//if e != nil {
// panic(e)
//}
} }
n, err = c.c.Read(b)
var hmacIn uint8
var payloadLen uint32
// Read the hmac LSB and payload len first
err = binary.Read(c.c, binary.BigEndian, &hmacIn)
if err != nil && err.Error() != "EOF" {
panic(err)
}
err = binary.Read(c.c, binary.BigEndian, &payloadLen)
if err != nil {
panic(err)
}
if payloadLen > 16384 {
panic("Insane payloadLen")
}
log.Println("payloadLen:", payloadLen)
var payloadBytes = make([]byte, payloadLen)
n, err = io.ReadFull(c.c, payloadBytes)
log.Print(" << Read ", n, " payloadBytes")
// Normal client 'exit' from interactive session will cause // Normal client 'exit' from interactive session will cause
// (on server side) err.Error() == "<iface/addr info ...>: use of closed network connection" // (on server side) err.Error() == "<iface/addr info ...>: use of closed network connection"
@ -322,26 +337,32 @@ func (c Conn) Read(b []byte) (n int, err error) {
} }
} }
log.Printf(" <:ctext:\r\n%s\r\n", hex.Dump(b[:n])) //EncodeToString(b[:n])) // print only used portion log.Printf(" <:ctext:\r\n%s\r\n", hex.Dump(payloadBytes[:n])) //EncodeToString(b[:n])) // print only used portion
db := bytes.NewBuffer(b[:n]) db := bytes.NewBuffer(payloadBytes[:n]) //copying payloadBytes to db
// The StreamReader acts like a pipe, decrypting // The StreamReader acts like a pipe, decrypting
// whatever is available and forwarding the result // whatever is available and forwarding the result
// to the parameter of Read() as a normal io.Reader // to the parameter of Read() as a normal io.Reader
rs := &cipher.StreamReader{S: c.r, R: db} rs := &cipher.StreamReader{S: c.r, R: db}
// FIXME: Possibly the bug here -- Read() may get grouped writes from // The caller isn't necessarily reading the full payload so we need
// server side, causing loss of hmac sync. -rlm 2018-01-16 // to decrypt ot an intermediate buffer, draining it on demand of caller
n, err = rs.Read(b) decryptN, err := rs.Read(payloadBytes)
log.Printf(" <-ptext:\r\n%s\r\n", hex.Dump(b[:n])) //EncodeToString(b[:n])) log.Printf(" <-ptext:\r\n%s\r\n", hex.Dump(payloadBytes[:n])) //EncodeToString(b[:n]))
if err != nil {
panic(err)
}
c.dBuf.Write(payloadBytes)
log.Printf("c.dBuf: %s\n", hex.Dump(c.dBuf.Bytes()))
// Re-calculate hmac, compare with received value // Re-calculate hmac, compare with received value
if c.hmacOn { c.rm.Write(payloadBytes)
c.rm.Write(b[:n])
hTmp := c.rm.Sum(nil)[0] hTmp := c.rm.Sum(nil)[0]
log.Printf("<%04x) HMAC:(i)%02x (c)%02x\r\n", len(b[:n]), hIn, hTmp) log.Printf("<%04x) HMAC:(i)%02x (c)%02x\r\n", decryptN, hmacIn, hTmp)
} }
log.Printf("Read() got %d bytes\n", c.dBuf.Len())
return copy(b, c.dBuf.Next(len(b)))
log.Printf("As Read() returns, c.dBuf is %d long: %s\n", c.dBuf.Len(), hex.Dump(c.dBuf.Bytes()))
return len(b), nil
} }
// Write a byte slice // Write a byte slice
@ -349,24 +370,18 @@ func (c Conn) Read(b []byte) (n int, err error) {
// See go doc io.Writer // See go doc io.Writer
func (c Conn) Write(b []byte) (n int, err error) { func (c Conn) Write(b []byte) (n int, err error) {
//log.Printf("[Encrypting...]\r\n") //log.Printf("[Encrypting...]\r\n")
//var pLen uint32 var hmacOut uint8
var hTmp = make([]byte, 1, 1) var payloadLen uint32
log.Printf(" :>ptext:\r\n%s\r\n", hex.Dump(b)) //EncodeToString(b)) log.Printf(" :>ptext:\r\n%s\r\n", hex.Dump(b)) //EncodeToString(b))
if c.hmacOn { payloadLen = uint32(len(b))
_ = hTmp
//pLen = uint32(len(b))
//_ = binary.Write(c.c, binary.BigEndian, &pLen)
// Calculate hmac on payload
c.wm.Write(b) c.wm.Write(b)
hTmp[0] = c.wm.Sum(nil)[0] hmacOut = uint8(c.wm.Sum(nil)[0])
//_, e := c.c.Write(hTmp)
//if e != nil { log.Printf(" (%04x> HMAC(o):%02x\r\n", payloadLen, hmacOut)
// panic(e)
//}
log.Printf(" (%04x> HMAC(o):%02x\r\n", len(b) /*pLen*/, hTmp)
}
var wb bytes.Buffer var wb bytes.Buffer
// The StreamWriter acts like a pipe, forwarding whatever is // The StreamWriter acts like a pipe, forwarding whatever is
@ -376,7 +391,11 @@ func (c Conn) Write(b []byte) (n int, err error) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
log.Printf(" ->ctext:\r\n%s\r\n", hex.Dump(wb.Bytes())) //EncodeToString(b)) // print only used portion log.Printf(" ->ctext:\r\n%s\r\n", hex.Dump(wb.Bytes()))
// Write hmac LSB, payloadLen followed by payload
_ = binary.Write(c.c, binary.BigEndian, hmacOut)
_ = binary.Write(c.c, binary.BigEndian, payloadLen)
n, err = c.c.Write(wb.Bytes()) n, err = c.c.Write(wb.Bytes())
if err != nil { if err != nil {