forked from lightningnetwork/lnd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conn.go
226 lines (194 loc) · 6.61 KB
/
conn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package brontide
import (
"bytes"
"io"
"math"
"net"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/lnwire"
)
// 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 Machine struct for
// additional details w.r.t to the handshake and encryption scheme.
type Conn struct {
conn net.Conn
noise *Machine
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, netAddr *lnwire.NetAddress,
dialer func(string, string) (net.Conn, error)) (*Conn, error) {
ipAddr := netAddr.Address.String()
var conn net.Conn
var err error
conn, err = dialer("tcp", ipAddr)
if err != nil {
return nil, err
}
b := &Conn{
conn: conn,
noise: NewBrontideMachine(true, localPriv, netAddr.IdentityKey),
}
// Initiate the handshake by sending the first act to the receiver.
actOne, err := b.noise.GenActOne()
if err != nil {
b.conn.Close()
return nil, err
}
if _, err := conn.Write(actOne[:]); err != nil {
b.conn.Close()
return nil, err
}
// We'll ensure that we get ActTwo from the remote peer in a timely
// manner. If they don't respond within 1s, then we'll kill the
// connection.
conn.SetReadDeadline(time.Now().Add(handshakeReadTimeout))
// 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 {
b.conn.Close()
return nil, err
}
if err := b.noise.RecvActTwo(actTwo); err != nil {
b.conn.Close()
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 {
b.conn.Close()
return nil, err
}
if _, err := conn.Write(actThree[:]); err != nil {
b.conn.Close()
return nil, err
}
// We'll reset the deadline as it's no longer critical beyond the
// initial handshake.
conn.SetReadDeadline(time.Time{})
return b, nil
}
// ReadNextMessage uses the connection in a message-oriented instructing it to
// read the next _full_ message with the brontide stream. This function will
// block until the read succeeds.
func (c *Conn) ReadNextMessage() ([]byte, error) {
return c.noise.ReadMessage(c.conn)
}
// Read reads data from the connection. Read can be made to time out and
// return an 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
// depleted, 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 an 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) {
// If the message doesn't require any chunking, then we can go ahead
// with a single write.
if len(b) <= math.MaxUint16 {
return len(b), c.noise.WriteMessage(c.conn, b)
}
// If we need to split the message into fragments, then we'll write
// chunks which maximize usage of the available payload.
chunkSize := math.MaxUint16
bytesToWrite := len(b)
bytesWritten := 0
for bytesWritten < bytesToWrite {
// If we're on the last chunk, then truncate the chunk size as
// necessary to avoid an out-of-bounds array memory access.
if bytesWritten+chunkSize > len(b) {
chunkSize = len(b) - bytesWritten
}
// Slice off the next chunk to be written based on our running
// counter and next chunk size.
chunk := b[bytesWritten : bytesWritten+chunkSize]
if err := c.noise.WriteMessage(c.conn, chunk); err != nil {
return bytesWritten, err
}
bytesWritten += len(chunk)
}
return bytesWritten, nil
}
// 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()
}