From 65febb5dcf226517efc7a4d24fb75e25ae277eb9 Mon Sep 17 00:00:00 2001 From: idk Date: Thu, 15 Dec 2022 23:52:05 +0000 Subject: [PATCH] Work on organizing this Noise over TCP Socket stuff --- lib/transport/noise/handshake.go | 20 +----- lib/transport/noise/i2np.go | 15 ++++ lib/transport/noise/incoming_handshake.go | 64 +++++++++++++++++ lib/transport/noise/noise_constants.go | 18 ++++- lib/transport/noise/outgoing_handshake.go | 62 +---------------- lib/transport/noise/read_session.go | 84 +++++++++++++++++++++++ lib/transport/noise/session.go | 67 +++++------------- lib/transport/noise/transport.go | 21 +++++- lib/transport/noise/write_session.go | 74 ++++++++++---------- 9 files changed, 253 insertions(+), 172 deletions(-) create mode 100644 lib/transport/noise/i2np.go create mode 100644 lib/transport/noise/incoming_handshake.go create mode 100644 lib/transport/noise/read_session.go diff --git a/lib/transport/noise/handshake.go b/lib/transport/noise/handshake.go index 607f77d..a5d7c55 100644 --- a/lib/transport/noise/handshake.go +++ b/lib/transport/noise/handshake.go @@ -4,26 +4,8 @@ import ( "sync" "github.com/go-i2p/go-i2p/lib/common/router_info" - "github.com/go-i2p/go-i2p/lib/transport" ) -func (c *NoiseTransport) getSession(routerInfo router_info.RouterInfo) (transport.TransportSession, error) { - session, err := c.GetSession(routerInfo) - if err != nil { - return nil, err - } - for { - if session.(*NoiseSession).handshakeComplete { - return nil, nil - } - if session.(*NoiseSession).Cond == nil { - break - } - session.(*NoiseSession).Cond.Wait() - } - return session, nil -} - func (c *NoiseTransport) Handshake(routerInfo router_info.RouterInfo) error { c.Mutex.Lock() defer c.Mutex.Unlock() @@ -38,7 +20,7 @@ func (c *NoiseTransport) Handshake(routerInfo router_info.RouterInfo) error { session.(*NoiseSession).Mutex.Lock() defer session.(*NoiseSession).Mutex.Unlock() c.Mutex.Lock() - // if c.config.isClient { + if err := session.(*NoiseSession).RunOutgoingHandshake(); err != nil { return err } diff --git a/lib/transport/noise/i2np.go b/lib/transport/noise/i2np.go new file mode 100644 index 0000000..41bd980 --- /dev/null +++ b/lib/transport/noise/i2np.go @@ -0,0 +1,15 @@ +package noise + +import "github.com/go-i2p/go-i2p/lib/i2np" + +func (s *NoiseSession) QueueSendI2NP(msg i2np.I2NPMessage) { + s.SendQueue.Enqueue(msg) +} + +func (s *NoiseSession) SendQueueSize() int { + return s.SendQueue.Size() +} + +func (s *NoiseSession) ReadNextI2NP() (i2np.I2NPMessage, error) { + return i2np.I2NPMessage{}, nil +} diff --git a/lib/transport/noise/incoming_handshake.go b/lib/transport/noise/incoming_handshake.go new file mode 100644 index 0000000..80d8835 --- /dev/null +++ b/lib/transport/noise/incoming_handshake.go @@ -0,0 +1,64 @@ +package noise + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "errors" + "io" + "log" + + "github.com/flynn/noise" +) + +func ComposeRecieverHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, ePrivate []byte) (negData, msg []byte, state *noise.HandshakeState, err error) { + if len(rs) != 0 && len(rs) != noise.DH25519.DHLen() { + return nil, nil, nil, errors.New("only 32 byte curve25519 public keys are supported") + } + negData = make([]byte, 6) + copy(negData, initNegotiationData(nil)) + pattern := noise.HandshakeXK + negData[5] = NOISE_PATTERN_XK + var random io.Reader + if len(ePrivate) == 0 { + random = rand.Reader + } else { + random = bytes.NewBuffer(ePrivate) + } + prologue := make([]byte, 2, uint16Size+len(negData)) + binary.BigEndian.PutUint16(prologue, uint16(len(negData))) + prologue = append(prologue, negData...) + //prologue = append(initString, prologue...) + state, err = noise.NewHandshakeState(noise.Config{ + StaticKeypair: s, + Initiator: false, + Pattern: pattern, + CipherSuite: noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256), + PeerStatic: rs, + Prologue: prologue, + Random: random, + }) + if err != nil { + return + } + padBuf := make([]byte, 2+len(payload)) + copy(padBuf[2:], payload) + msg, _, _, err = state.WriteMessage(msg, padBuf) + return +} + +func (c *NoiseSession) RunIncomingHandshake() error { + negData, msg, state, err := ComposeRecieverHandshakeMessage(c.HandKey, nil, nil, nil) + if err != nil { + return err + } + if _, err = c.Write(negData); err != nil { + return err + } + if _, err = c.Write(msg); err != nil { + return err + } + log.Println(state) + c.handshakeComplete = true + return nil +} diff --git a/lib/transport/noise/noise_constants.go b/lib/transport/noise/noise_constants.go index 1984c57..7fdab54 100644 --- a/lib/transport/noise/noise_constants.go +++ b/lib/transport/noise/noise_constants.go @@ -1,7 +1,7 @@ package noise import ( - "math" + "encoding/binary" "github.com/flynn/noise" ) @@ -16,8 +16,8 @@ const ( NOISE_PATTERN_XK = 11 - uint16Size = 2 // uint16 takes 2 bytes - MaxPayloadSize = math.MaxUint16 - 16 /*mac size*/ - uint16Size /*data len*/ + uint16Size = 2 // uint16 takes 2 bytes + MaxPayloadSize = 65537 ) var ciphers = map[byte]noise.CipherFunc{ @@ -32,3 +32,15 @@ var hashes = map[byte]noise.HashFunc{ var patterns = map[byte]noise.HandshakePattern{ NOISE_PATTERN_XK: noise.HandshakeXK, } + +func initNegotiationData(negotiationData []byte) []byte { + if negotiationData != nil { + return negotiationData + } + negotiationData = make([]byte, 6) + binary.BigEndian.PutUint16(negotiationData, 1) //version + negotiationData[2] = NOISE_DH_CURVE25519 + negotiationData[3] = NOISE_CIPHER_CHACHAPOLY + negotiationData[4] = NOISE_HASH_SHA256 + return negotiationData +} diff --git a/lib/transport/noise/outgoing_handshake.go b/lib/transport/noise/outgoing_handshake.go index d20ad65..5786924 100644 --- a/lib/transport/noise/outgoing_handshake.go +++ b/lib/transport/noise/outgoing_handshake.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "errors" "io" + "log" "github.com/flynn/noise" ) @@ -49,7 +50,7 @@ func ComposeInitiatorHandshakeMessage(s noise.DHKey, rs []byte, payload []byte, } func (c *NoiseSession) RunOutgoingHandshake() error { - negData, msg, state, err := ComposeInitiatorHandshakeMessage(c.DHKey, nil, nil, nil) + negData, msg, state, err := ComposeInitiatorHandshakeMessage(c.HandKey, nil, nil, nil) if err != nil { return err } @@ -59,64 +60,7 @@ func (c *NoiseSession) RunOutgoingHandshake() error { if _, err = c.Write(msg); err != nil { return err } - //read negotiation data - if i2np, err := c.ReadNextI2NP(); err != nil { - return err - } else { - c.RecvQueue.Enqueue(i2np) - } - negotiationData := c.handshakeBuffer.Next(c.handshakeBuffer.Len()) - //read noise message - if i2np, err := c.ReadNextI2NP(); err != nil { - return err - } else { - c.RecvQueue.Enqueue(i2np) - } - msg = c.handshakeBuffer.Next(c.handshakeBuffer.Len()) - if len(negotiationData) != 0 || len(msg) == 0 { - return errors.New("Server returned error") - } - //cannot reuse msg for read, need another buf - inBlock := newBlock() - //inBlock.reserve(len(msg)) - var payload []byte - payload, c.CipherState, c.NoiseTransport.CipherState, err = state.ReadMessage(inBlock, msg) - if err != nil { - //c.NoiseTransport.freeBlock(inBlock) - return err - } - err = c.processCallback(state.PeerStatic(), payload) - /*if err != nil { - c.NoiseTransport.freeBlock(inBlock) - return err - }*/ - /*c.NoiseTransport.freeBlock(inBlock) - if c.CipherState == nil && c.NoiseTransport.CipherState == nil { - b := c.newBlock() - if b.data, c.CipherState, c.NoiseTransport.CipherState, err = state.WriteMessage(b.data, pad(c.config.Payload)); err != nil { - c.freeBlock(b) - return err - } - if _, err = c.Write(nil); err != nil { - c.freeBlock(b) - return err - } - if _, err = c.Write(b.data); err != nil { - c.freeBlock(b) - return err - } - c.freeBlock(b) - if c.CipherState == nil || c.NoiseTransport.CipherState == nil { - log.WithFields(log.Fields{ - "at": "(NoiseSession) RunIncomingHandshake", - "reason": "unsupported session", - }).Error("unsupported session") - return errors.New("unsupported session") - } - } - */ - //c.in.padding, c.out.padding = c.config.Padding, c.config.Padding - //c.channelBinding = state.ChannelBinding() + log.Println(state) c.handshakeComplete = true return nil } diff --git a/lib/transport/noise/read_session.go b/lib/transport/noise/read_session.go new file mode 100644 index 0000000..5510311 --- /dev/null +++ b/lib/transport/noise/read_session.go @@ -0,0 +1,84 @@ +package noise + +import ( + "errors" + "sync/atomic" + + log "github.com/sirupsen/logrus" +) + +func (c *NoiseSession) Read(b []byte) (int, error) { + // interlock with Close below + for { + x := atomic.LoadInt32(&c.activeCall) + if x&1 != 0 { + log.WithFields(log.Fields{ + "at": "(NoiseSession) Read", + "reason": "session is closed", + }).Error("session is closed") + return 0, errors.New("session is closed") + } + if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) { + defer atomic.AddInt32(&c.activeCall, -2) + break + } + } + if !c.handshakeComplete { + if err := c.RunIncomingHandshake(); err != nil { + return 0, err + } + } + c.Mutex.Lock() + defer c.Mutex.Unlock() + if !c.handshakeComplete { + return 0, errors.New("internal error") + } + n, err := c.readPacketLocked(b) + return n, err +} + +func (c *NoiseSession) decryptPacket(data []byte) (int, []byte, error) { + m := len(data) + /*packet := c.InitializePacket() + maxPayloadSize := c.maxPayloadSizeForRead(packet) + if m > int(maxPayloadSize) { + m = int(maxPayloadSize) + } + if c.CipherState != nil { + ////fmt.Println("writing encrypted packet:", m) + packet.reserve(uint16Size + uint16Size + m + macSize) + packet.resize(uint16Size + uint16Size + m) + copy(packet.data[uint16Size+uint16Size:], data[:m]) + binary.BigEndian.PutUint16(packet.data[uint16Size:], uint16(m)) + //fmt.Println("encrypt size", uint16(m)) + } else { + packet.resize(len(packet.data) + len(data)) + copy(packet.data[uint16Size:len(packet.data)], data[:m]) + binary.BigEndian.PutUint16(packet.data, uint16(len(data))) + } + b := c.encryptIfNeeded(packet)*/ + //c.freeBlock(packet) + return m, data, nil +} + +func (c *NoiseSession) readPacketLocked(data []byte) (int, error) { + var n int + if len(data) == 0 { //special case to answer when everything is ok during handshake + if _, err := c.Conn.Read(make([]byte, 2)); err != nil { + return 0, err + } + } + for len(data) > 0 { + m, b, err := c.encryptPacket(data) + if err != nil { + return 0, err + } + if n, err := c.Conn.Read(b); err != nil { + return n, err + } else { + n += m + data = data[m:] + } + } + return n, nil +} diff --git a/lib/transport/noise/session.go b/lib/transport/noise/session.go index d9a6772..8938039 100644 --- a/lib/transport/noise/session.go +++ b/lib/transport/noise/session.go @@ -9,10 +9,8 @@ import ( cb "github.com/emirpasic/gods/queues/circularbuffer" "github.com/flynn/noise" - log "github.com/sirupsen/logrus" "github.com/go-i2p/go-i2p/lib/common/router_info" - "github.com/go-i2p/go-i2p/lib/i2np" "github.com/go-i2p/go-i2p/lib/transport" ) @@ -21,10 +19,12 @@ type NoiseSession struct { *noise.CipherState sync.Mutex *sync.Cond - *NoiseTransport - noise.DHKey + *NoiseTransport // The parent transport, which "Dialed" the connection to the peer whith whom we established the session RecvQueue *cb.Queue SendQueue *cb.Queue + SendKey noise.DHKey + RecvKey noise.DHKey + HandKey noise.DHKey VerifyCallback VerifyCallbackFunc handshakeBuffer bytes.Buffer activeCall int32 @@ -32,11 +32,6 @@ type NoiseSession struct { Conn net.Conn } -// Read implements net.Conn -func (noise_session *NoiseSession) Read(b []byte) (n int, err error) { - return noise_session.Conn.Read(b) -} - // RemoteAddr implements net.Conn func (noise_session *NoiseSession) RemoteAddr() net.Addr { return &noise_session.RouterInfo @@ -64,18 +59,6 @@ func (s *NoiseSession) LocalAddr() net.Addr { return s.Conn.LocalAddr() } -func (s *NoiseSession) QueueSendI2NP(msg i2np.I2NPMessage) { - s.SendQueue.Enqueue(msg) -} - -func (s *NoiseSession) SendQueueSize() int { - return s.SendQueue.Size() -} - -func (s *NoiseSession) ReadNextI2NP() (i2np.I2NPMessage, error) { - return i2np.I2NPMessage{}, nil -} - func (s *NoiseSession) Close() error { s.SendQueue.Clear() s.RecvQueue.Clear() @@ -98,34 +81,18 @@ func newBlock() []byte { type VerifyCallbackFunc func(publicKey []byte, data []byte) error func NewNoiseTransportSession(ri router_info.RouterInfo) (transport.TransportSession, error) { - socket, err := DialNoise("noise", ri) - if err != nil { - return nil, err + //socket, err := DialNoise("noise", ri) + for _, addr := range ri.RouterAddresses() { + socket, err := net.Dial("tcp", string(addr.Bytes())) + if err != nil { + return nil, err + } + return &NoiseSession{ + SendQueue: cb.New(1024), + RecvQueue: cb.New(1024), + RouterInfo: ri, + Conn: socket, + }, nil } - return &NoiseSession{ - SendQueue: cb.New(1024), - RecvQueue: cb.New(1024), - RouterInfo: ri, - Conn: socket, - }, nil -} - -// DialNoise initiates a session with a remote Noise transport, using a -// routerinfo to derive the address to connect to. It doesn't have any chance of -// working yet. -func DialNoise(network string, addr router_info.RouterInfo) (net.Conn, error) { - for _, addr := range addr.RouterAddresses() { - log.WithFields(log.Fields{ - "at": "(DialNoise)", - "addr": addr, - }).Error("error parsing router info") - return Dial(string(addr.TransportStyle()), "") - } - return nil, fmt.Errorf("No valid transport discovered.") -} - -// Dial initiates a session with a remote Noise transport at a host:port -// or ip:port -func Dial(network, addr string) (net.Conn, error) { - return net.Dial("tcp", addr) + return nil, fmt.Errorf("Transport constructor error") } diff --git a/lib/transport/noise/transport.go b/lib/transport/noise/transport.go index 9d0b8b0..4302407 100644 --- a/lib/transport/noise/transport.go +++ b/lib/transport/noise/transport.go @@ -20,9 +20,9 @@ import ( ) type NoiseTransport struct { - *noise.CipherState - router_identity.RouterIdentity sync.Mutex + router_identity.RouterIdentity + *noise.CipherState Listener net.Listener peerConnections map[data.Hash]transport.TransportSession } @@ -86,6 +86,23 @@ func (noopt *NoiseTransport) GetSession(routerInfo router_info.RouterInfo) (tran return nil, err } +func (c *NoiseTransport) getSession(routerInfo router_info.RouterInfo) (transport.TransportSession, error) { + session, err := c.GetSession(routerInfo) + if err != nil { + return nil, err + } + for { + if session.(*NoiseSession).handshakeComplete { + return nil, nil + } + if session.(*NoiseSession).Cond == nil { + break + } + session.(*NoiseSession).Cond.Wait() + } + return session, nil +} + // Compatable return true if a routerInfo is compatable with this transport func (noopt *NoiseTransport) Compatable(routerInfo router_info.RouterInfo) bool { _, ok := noopt.peerConnections[routerInfo.IdentHash()] diff --git a/lib/transport/noise/write_session.go b/lib/transport/noise/write_session.go index 508d2aa..fd5a065 100644 --- a/lib/transport/noise/write_session.go +++ b/lib/transport/noise/write_session.go @@ -1,7 +1,6 @@ package noise import ( - "encoding/binary" "errors" "sync/atomic" @@ -24,8 +23,10 @@ func (c *NoiseSession) Write(b []byte) (int, error) { break } } - if err := c.RunOutgoingHandshake(); err != nil { - return 0, err + if !c.handshakeComplete { + if err := c.RunOutgoingHandshake(); err != nil { + return 0, err + } } c.Mutex.Lock() defer c.Mutex.Unlock() @@ -36,6 +37,30 @@ func (c *NoiseSession) Write(b []byte) (int, error) { return n, err } +func (c *NoiseSession) encryptPacket(data []byte) (int, []byte, error) { + m := len(data) + /*packet := c.InitializePacket() + maxPayloadSize := c.maxPayloadSizeForWrite(packet) + if m > int(maxPayloadSize) { + m = int(maxPayloadSize) + } + if c.CipherState != nil { + ////fmt.Println("writing encrypted packet:", m) + packet.reserve(uint16Size + uint16Size + m + macSize) + packet.resize(uint16Size + uint16Size + m) + copy(packet.data[uint16Size+uint16Size:], data[:m]) + binary.BigEndian.PutUint16(packet.data[uint16Size:], uint16(m)) + //fmt.Println("encrypt size", uint16(m)) + } else { + packet.resize(len(packet.data) + len(data)) + copy(packet.data[uint16Size:len(packet.data)], data[:m]) + binary.BigEndian.PutUint16(packet.data, uint16(len(data))) + } + b := c.encryptIfNeeded(packet)*/ + //c.freeBlock(packet) + return m, data, nil +} + func (c *NoiseSession) writePacketLocked(data []byte) (int, error) { var n int if len(data) == 0 { //special case to answer when everything is ok during handshake @@ -44,45 +69,16 @@ func (c *NoiseSession) writePacketLocked(data []byte) (int, error) { } } for len(data) > 0 { - m := len(data) - packet := c.InitializePacket() - maxPayloadSize := c.maxPayloadSizeForWrite(packet) - if m > int(maxPayloadSize) { - m = int(maxPayloadSize) + m, b, err := c.encryptPacket(data) + if err != nil { + return 0, err } - if c.CipherState != nil { - ////fmt.Println("writing encrypted packet:", m) - packet.reserve(uint16Size + uint16Size + m + macSize) - packet.resize(uint16Size + uint16Size + m) - copy(packet.data[uint16Size+uint16Size:], data[:m]) - binary.BigEndian.PutUint16(packet.data[uint16Size:], uint16(m)) - //fmt.Println("encrypt size", uint16(m)) - } else { - packet.resize(len(packet.data) + len(data)) - copy(packet.data[uint16Size:len(packet.data)], data[:m]) - binary.BigEndian.PutUint16(packet.data, uint16(len(data))) - } - b := c.encryptIfNeeded(packet) - c.freeBlock(packet) - ////fmt.Println(hex.EncodeToString(b)) - if _, err := c.conn.Write(b); err != nil { + if n, err := c.Conn.Write(b); err != nil { return n, err + } else { + n += m + data = data[m:] } - n += m - data = data[m:] - } return n, nil } - -func initNegotiationData(negotiationData []byte) []byte { - if negotiationData != nil { - return negotiationData - } - negotiationData = make([]byte, 6) - binary.BigEndian.PutUint16(negotiationData, 1) //version - negotiationData[2] = NOISE_DH_CURVE25519 - negotiationData[3] = NOISE_CIPHER_CHACHAPOLY - negotiationData[4] = NOISE_HASH_SHA256 - return negotiationData -}