package brontide import ( "bytes" "io" "net" "time" "github.com/roasbeef/btcd/btcec" ) // Conn is an implementation of net.Conn which enforces an authenticated key // exchange and message encryption protocol dubbed "Brontide" after initial TCP // connection establishment. In the case of a successful handshake, all // messages sent via the .Write() method are encrypted with an AEAD cipher // along with an encrypted length-prefix. See the BrontideMachine struct for // additional details w.r.t to the handshake and encryption scheme. type Conn struct { conn net.Conn noise *BrontideMachine readBuf bytes.Buffer } // A compile-time assertion to ensure that Conn meets the net.Conn interface. var _ net.Conn = (*Conn)(nil) // Dial attempts to establish an encrypted+authenticated connection with the // remote peer located at address which has remotePub as its long-term static // public key. In the case of a handshake failure, the connection is closed and // a non-nil error is returned. func Dial(localPriv *btcec.PrivateKey, remotePub *btcec.PublicKey, address string) (*Conn, error) { conn, err := net.Dial("tcp", address) if err != nil { return nil, err } b := &Conn{ conn: conn, noise: NewBrontideMachine(true, localPriv, remotePub), } // Initiate the handshake by sending the first act to the receiver. actOne, err := b.noise.GenActOne() if err != nil { return nil, err } if _, err := conn.Write(actOne[:]); err != nil { return nil, err } // If the first act was successful (we know that address is actually // remotePub), then read the second act after which we'll be able to // send our static public key to the remote peer with strong forward // secrecy. var actTwo [ActTwoSize]byte if _, err := io.ReadFull(conn, actTwo[:]); err != nil { return nil, err } if err := b.noise.RecvActTwo(actTwo); err != nil { return nil, err } // Finally, complete the handshake by sending over our encrypted static // key and execute the final ECDH operation. actThree, err := b.noise.GenActThree() if err != nil { return nil, err } if _, err := conn.Write(actThree[:]); err != nil { return nil, err } return b, nil } // Read reads data from the connection. Read can be made to time out and // return a Error with Timeout() == true after a fixed time limit; see // SetDeadline and SetReadDeadline. // // Part of the net.Conn interface. func (c *Conn) Read(b []byte) (n int, err error) { // In order to reconcile the differences between the record abstraction of // our AEAD connection, and the stream abstraction of TCP, we maintain an // intermediate read buffer. If this buffer becomes depleated, then we read // the next record, and feed it into the buffer. Otherwise, we read // directly from the buffer. if c.readBuf.Len() == 0 { plaintext, err := c.noise.ReadMessage(c.conn) if err != nil { return 0, err } if _, err := c.readBuf.Write(plaintext); err != nil { return 0, err } } return c.readBuf.Read(b) } // Write writes data to the connection. Write can be made to time out and // return a Error with Timeout() == true after a fixed time limit; see // SetDeadline and SetWriteDeadline. // // Part of the net.Conn interface. func (c *Conn) Write(b []byte) (n int, err error) { return len(b), c.noise.WriteMessage(c.conn, b) } // Close closes the connection. Any blocked Read or Write operations will be // unblocked and return errors. // // Part of the net.Conn interface. func (c *Conn) Close() error { // TODO(roasbeef): reset brontide state? return c.conn.Close() } // LocalAddr returns the local network address. // // Part of the net.Conn interface. func (c *Conn) LocalAddr() net.Addr { return c.conn.LocalAddr() } // RemoteAddr returns the remote network address. // // Part of the net.Conn interface. func (c *Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } // SetDeadline sets the read and write deadlines associated with the // connection. It is equivalent to calling both SetReadDeadline and // SetWriteDeadline. // // Part of the net.Conn interface. func (c *Conn) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) } // SetReadDeadline sets the deadline for future Read calls. A zero value for t // means Read will not time out. // // Part of the net.Conn interface. func (c *Conn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } // SetWriteDeadline sets the deadline for future Write calls. Even if write // times out, it may return n > 0, indicating that some of the data was // successfully written. A zero value for t means Write will not time out. // // Part of the net.Conn interface. func (c *Conn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) } // RemotePub returns the remote peer's static public key. func (c *Conn) RemotePub() *btcec.PublicKey { return c.noise.remoteStatic } // LocalPub returns the local peer's static public key. func (c *Conn) LocalPub() *btcec.PublicKey { return c.noise.localStatic.PubKey() }