-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrw.go
155 lines (128 loc) · 4.7 KB
/
rw.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
package go_libp2p_cert
import (
"encoding/binary"
"io"
pool "github.com/libp2p/go-buffer-pool"
"golang.org/x/crypto/poly1305"
)
// MaxTransportMsgLength is the Noise-imposed maximum transport message length,
// inclusive of the MAC size (16 bytes, Poly1305 for noise-libp2p).
const MaxTransportMsgLength = 0xffff
// MaxPlaintextLength is the maximum payload size. It is MaxTransportMsgLength
// minus the MAC size. Payloads over this size will be automatically chunked.
const MaxPlaintextLength = MaxTransportMsgLength - poly1305.TagSize
// LengthPrefixLength is the length of the length prefix itself, which precedes
// all transport messages in order to delimit them. In bytes.
const LengthPrefixLength = 2
// Read reads from the secure connection, returning plaintext data in `buf`.
//
// Honours io.Reader in terms of behaviour.
func (s *secureSession) Read(buf []byte) (int, error) {
s.readLock.Lock()
defer s.readLock.Unlock()
// 1. If we have queued received bytes:
// 1a. If len(buf) < len(queued), saturate buf, update seek pointer, return.
// 1b. If len(buf) >= len(queued), copy remaining to buf, release queued buffer back into pool, return.
//
// 2. Else, read the next message off the wire; next_len is length prefix.
// 2a. If len(buf) >= next_len, copy the message to input buffer (zero-alloc path), and return.
// 2b. If len(buf) >= (next_len - length of Authentication Tag), get buffer from pool, read encrypted message into it.
// decrypt message directly into the input buffer and return the buffer obtained from the pool.
// 2c. If len(buf) < next_len, obtain buffer from pool, copy entire message into it, saturate buf, update seek pointer.
if s.qbuf != nil {
// we have queued bytes; copy as much as we can.
copied := copy(buf, s.qbuf[s.qseek:])
s.qseek += copied
if s.qseek == len(s.qbuf) {
// queued buffer is now empty, reset and release.
pool.Put(s.qbuf)
s.qseek, s.qbuf = 0, nil
}
return copied, nil
}
// length of the next encrypted message.
nextMsgLen, err := s.readNextInsecureMsgLen()
if err != nil {
return 0, err
}
// If the buffer is atleast as big as the encrypted message size,
// we can read AND decrypt in place.
if len(buf) >= nextMsgLen {
if err := s.readNextMsgInsecure(buf[:nextMsgLen]); err != nil {
return 0, err
}
dbuf, err := s.decrypt(buf[:0], buf[:nextMsgLen])
if err != nil {
return 0, err
}
return len(dbuf), nil
}
// otherwise, we get a buffer from the pool so we can read the message into it
// and then decrypt in place, since we're retaining the buffer (or a view thereof).
cbuf := pool.Get(nextMsgLen)
if err := s.readNextMsgInsecure(cbuf); err != nil {
return 0, err
}
if s.qbuf, err = s.decrypt(cbuf[:0], cbuf); err != nil {
return 0, err
}
// copy as many bytes as we can; update seek pointer.
s.qseek = copy(buf, s.qbuf)
return s.qseek, nil
}
// Write encrypts the plaintext `in` data and sends it on the
// secure connection.
func (s *secureSession) Write(data []byte) (int, error) {
s.writeLock.Lock()
defer s.writeLock.Unlock()
var (
written int
cbuf []byte
total = len(data)
)
if total < MaxPlaintextLength {
cbuf = pool.Get(total + poly1305.TagSize + LengthPrefixLength)
} else {
cbuf = pool.Get(MaxTransportMsgLength + LengthPrefixLength)
}
defer pool.Put(cbuf)
for written < total {
end := written + MaxPlaintextLength
if end > total {
end = total
}
b, err := s.encrypt(cbuf[:LengthPrefixLength], data[written:end])
if err != nil {
return 0, err
}
binary.BigEndian.PutUint16(b, uint16(len(b)-LengthPrefixLength))
_, err = s.writeMsgInsecure(b)
if err != nil {
return written, err
}
written = end
}
return written, nil
}
// readNextInsecureMsgLen reads the length of the next message on the insecureConn channel.
func (s *secureSession) readNextInsecureMsgLen() (int, error) {
_, err := io.ReadFull(s.insecureReader, s.rlen[:])
if err != nil {
return 0, err
}
return int(binary.BigEndian.Uint16(s.rlen[:])), err
}
// readNextMsgInsecure tries to read exactly len(buf) bytes into buf from
// the insecureConn channel and returns the error, if any.
// Ideally, for reading a message, you'd first want to call `readNextInsecureMsgLen`
// to determine the size of the next message to be read from the insecureConn channel and then call
// this function with a buffer of exactly that size.
func (s *secureSession) readNextMsgInsecure(buf []byte) error {
_, err := io.ReadFull(s.insecureReader, buf)
return err
}
// writeMsgInsecure writes to the insecureConn conn.
// data will be prefixed with its length in bytes, written as a 16-bit uint in network order.
func (s *secureSession) writeMsgInsecure(data []byte) (int, error) {
return s.insecureConn.Write(data)
}