Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

weaveworks / weave / #6002

13 Apr 2016 - 6:49 coverage decreased (-0.1%) to 75.607%
#6002

Pull #2154

circleci

2f1e5f233f2b7283a9bf3277e75bf30a?size=18&default=identiconrade
Lock round TestRouter map accesses, and copy the set where we may block.
Pull Request #2154: Lock round TestRouter map accesses

6137 of 8117 relevant lines covered (75.61%)

129553.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.44
/router/frame_crypto.go
1
package router
2

3
import (
4
        "encoding/binary"
5
        "fmt"
6

7
        "github.com/andybalholm/go-bit"
8
        "golang.org/x/crypto/nacl/secretbox"
9
)
10

11
// Frame Encryptors
12

13
type Encryptor interface {
14
        FrameOverhead() int
15
        PacketOverhead() int
16
        Bytes() ([]byte, error)
17
        AppendFrame(src []byte, dst []byte, frame []byte)
18
        TotalLen() int
19
}
20

21
type NonEncryptor struct {
22
        buf       []byte
23
        bufTail   []byte
24
        buffered  int
25
        prefixLen int
26
}
27

28
type NaClEncryptor struct {
29
        NonEncryptor
30
        buf        []byte
31
        prefixLen  int
32
        sessionKey *[32]byte
33
        nonce      [24]byte
34
        seqNo      uint64
35
        df         bool
36
}
37

38
func NewNonEncryptor(prefix []byte) *NonEncryptor {
142×
39
        buf := make([]byte, MaxUDPPacketSize)
142×
40
        prefixLen := copy(buf, prefix)
142×
41
        return &NonEncryptor{
142×
42
                buf:       buf,
142×
43
                bufTail:   buf[prefixLen:],
142×
44
                buffered:  prefixLen,
142×
45
                prefixLen: prefixLen}
142×
46
}
142×
47

48
func (ne *NonEncryptor) PacketOverhead() int {
80×
49
        return ne.prefixLen
80×
50
}
80×
51

52
func (ne *NonEncryptor) FrameOverhead() int {
246×
53
        return NameSize + NameSize + 2
246×
54
}
246×
55

56
func (ne *NonEncryptor) Bytes() ([]byte, error) {
495×
57
        buf := ne.buf[:ne.buffered]
495×
58
        ne.buffered = ne.prefixLen
495×
59
        ne.bufTail = ne.buf[ne.prefixLen:]
495×
60
        return buf, nil
495×
61
}
495×
62

63
func (ne *NonEncryptor) AppendFrame(src []byte, dst []byte, frame []byte) {
509×
64
        bufTail := ne.bufTail
509×
65
        srcLen := copy(bufTail, src)
509×
66
        bufTail = bufTail[srcLen:]
509×
67
        dstLen := copy(bufTail, dst)
509×
68
        bufTail = bufTail[dstLen:]
509×
69
        binary.BigEndian.PutUint16(bufTail, uint16(len(frame)))
509×
70
        bufTail = bufTail[2:]
509×
71
        copy(bufTail, frame)
509×
72
        ne.bufTail = bufTail[len(frame):]
509×
73
        ne.buffered += srcLen + dstLen + 2 + len(frame)
509×
74
}
509×
75

76
func (ne *NonEncryptor) TotalLen() int {
175×
77
        return ne.buffered
175×
78
}
175×
79

80
func NewNaClEncryptor(prefix []byte, sessionKey *[32]byte, outbound bool, df bool) *NaClEncryptor {
2×
81
        buf := make([]byte, MaxUDPPacketSize)
2×
82
        prefixLen := copy(buf, prefix)
2×
83
        ne := &NaClEncryptor{
2×
84
                NonEncryptor: *NewNonEncryptor([]byte{}),
2×
85
                buf:          buf,
2×
86
                prefixLen:    prefixLen,
2×
87
                sessionKey:   sessionKey,
2×
88
                df:           df}
2×
89
        if outbound {
2×
UNCOV
90
                ne.nonce[0] |= (1 << 7)
!
UNCOV
91
        }
!
92
        return ne
2×
93
}
94

95
func (ne *NaClEncryptor) Bytes() ([]byte, error) {
12×
96
        plaintext, err := ne.NonEncryptor.Bytes()
12×
97
        if err != nil {
12×
98
                return nil, err
!
99
        }
!
100
        // We carry the DF flag in the (unencrypted portion of the)
101
        // payload, rather than just extracting it from the packet headers
102
        // at the receiving end, since we do not trust routers not to mess
103
        // with headers. As we have different decryptors for non-DF and
104
        // DF, that would result in hard to track down packet drops due to
105
        // crypto errors.
106
        seqNoAndDF := ne.seqNo
12×
107
        if ne.df {
16×
108
                seqNoAndDF |= (1 << 63)
4×
109
        }
4×
110
        ciphertext := ne.buf
12×
111
        binary.BigEndian.PutUint64(ciphertext[ne.prefixLen:], seqNoAndDF)
12×
112
        binary.BigEndian.PutUint64(ne.nonce[16:24], seqNoAndDF)
12×
113
        // Seal *appends* to ciphertext
12×
114
        ciphertext = secretbox.Seal(ciphertext[:ne.prefixLen+8], plaintext, &ne.nonce, ne.sessionKey)
12×
115
        ne.seqNo++
12×
116
        return ciphertext, nil
12×
117
}
118

119
func (ne *NaClEncryptor) PacketOverhead() int {
10×
120
        return ne.prefixLen + 8 + secretbox.Overhead + ne.NonEncryptor.PacketOverhead()
10×
121
}
10×
122

123
func (ne *NaClEncryptor) TotalLen() int {
9×
124
        return ne.PacketOverhead() + ne.NonEncryptor.TotalLen()
9×
125
}
9×
126

127
// Frame Decryptors
128

129
type FrameConsumer func(src []byte, dst []byte, frame []byte)
130

131
type Decryptor interface {
132
        IterateFrames([]byte, FrameConsumer) error
133
}
134

135
type NonDecryptor struct {
136
}
137

138
type NaClDecryptor struct {
139
        NonDecryptor
140
        sessionKey *[32]byte
141
        instance   *NaClDecryptorInstance
142
        instanceDF *NaClDecryptorInstance
143
}
144

145
type NaClDecryptorInstance struct {
146
        nonce               [24]byte
147
        currentWindow       uint64
148
        usedOffsets         *bit.Set
149
        previousUsedOffsets *bit.Set
150
}
151

152
func NewNaClDecryptorInstance(outbound bool) *NaClDecryptorInstance {
2×
153
        di := &NaClDecryptorInstance{usedOffsets: bit.New()}
2×
154
        if !outbound {
4×
155
                di.nonce[0] |= (1 << 7)
2×
156
        }
2×
157
        return di
2×
158
}
159

160
type PacketDecodingError struct {
161
        Desc string
162
}
163

164
func (pde PacketDecodingError) Error() string {
!
165
        return fmt.Sprint("Failed to decode packet: ", pde.Desc)
!
166
}
!
167

168
func NewNonDecryptor() *NonDecryptor {
71×
169
        return &NonDecryptor{}
71×
170
}
71×
171

172
func (nd *NonDecryptor) IterateFrames(packet []byte, consumer FrameConsumer) error {
372×
173
        for len(packet) >= (2 + NameSize + NameSize) {
758×
174
                srcNameByte := packet[:NameSize]
386×
175
                packet = packet[NameSize:]
386×
176
                dstNameByte := packet[:NameSize]
386×
177
                packet = packet[NameSize:]
386×
178
                length := binary.BigEndian.Uint16(packet[:2])
386×
179
                packet = packet[2:]
386×
180
                if len(packet) < int(length) {
386×
181
                        return PacketDecodingError{Desc: fmt.Sprintf("too short; expected frame of length %d, got %d", length, len(packet))}
!
182
                }
!
183
                frame := packet[:length]
386×
184
                packet = packet[length:]
386×
185
                consumer(srcNameByte, dstNameByte, frame)
386×
186
        }
187
        if len(packet) > 0 {
372×
188
                return PacketDecodingError{Desc: fmt.Sprintf("%d octets of trailing garbage", len(packet))}
!
189
        }
!
190
        return nil
372×
191
}
192

193
func NewNaClDecryptor(sessionKey *[32]byte, outbound bool) *NaClDecryptor {
1×
194
        return &NaClDecryptor{
1×
195
                NonDecryptor: *NewNonDecryptor(),
1×
196
                sessionKey:   sessionKey,
1×
197
                instance:     NewNaClDecryptorInstance(outbound),
1×
198
                instanceDF:   NewNaClDecryptorInstance(outbound)}
1×
199
}
1×
200

201
func (nd *NaClDecryptor) IterateFrames(packet []byte, consumer FrameConsumer) error {
10×
202
        if len(packet) < 8 {
10×
203
                return PacketDecodingError{Desc: fmt.Sprintf("encrypted UDP packet too short; expected length >= 8, got %d", len(packet))}
!
204
        }
!
205
        buf, success := nd.decrypt(packet)
10×
206
        if !success {
10×
207
                return PacketDecodingError{Desc: fmt.Sprint("UDP packet decryption failed")}
!
208
        }
!
209
        return nd.NonDecryptor.IterateFrames(buf, consumer)
10×
210
}
211

212
func (nd *NaClDecryptor) decrypt(buf []byte) ([]byte, bool) {
10×
213
        seqNoAndDF := binary.BigEndian.Uint64(buf[:8])
10×
214
        df := (seqNoAndDF & (1 << 63)) != 0
10×
215
        seqNo := seqNoAndDF & ((1 << 63) - 1)
10×
216
        var di *NaClDecryptorInstance
10×
217
        if df {
12×
218
                di = nd.instanceDF
2×
219
        } else {
10×
220
                di = nd.instance
8×
221
        }
8×
222
        binary.BigEndian.PutUint64(di.nonce[16:24], seqNoAndDF)
10×
223
        result, success := secretbox.Open(nil, buf[8:], &di.nonce, nd.sessionKey)
10×
224
        if !success {
10×
225
                return nil, false
!
226
        }
!
227
        // Drop duplicates. We do this *after* decryption since we must
228
        // not advance our state unless decryption succeeded. Doing so
229
        // would open an easy attack vector where an adversary could
230
        // inject a packet with a sequence number of (1 << 63) - 1,
231
        // causing all subsequent genuine packets to get dropped.
232
        offset, usedOffsets := di.advanceState(seqNo)
10×
233
        if usedOffsets == nil || usedOffsets.Contains(offset) {
10×
234
                // We have detected a possible replay attack, but it is
!
235
                // possible we may have just received a very old packet, or
!
236
                // duplication may have occurred in the network. So let's just
!
237
                // drop the packet silently.
!
238
                return nil, true
!
239
        }
!
240
        usedOffsets.Add(offset)
10×
241
        return result, success
10×
242
}
243

244
// We record seen message sequence numbers in a sliding window of
245
// 2*WindowSize which slides in WindowSize increments. This allows us
246
// to process out-of-order delivery within the window, while
247
// accurately discarding duplicates. By contrast any messages with
248
// sequence numbers below the window are discarded as potential
249
// duplicates.
250
//
251
// There are two sets, corresponding to the lower and upper half of
252
// the window. We slide the window s.t. that 2nd set always contains
253
// the highest seen sequence number. We do this regardless of how far
254
// ahead of the current window that sequence number might be, so we
255
// can cope with large gaps resulting from packet loss.
256

257
const (
258
        WindowSize = 20 // bits
259
)
260

261
func (di *NaClDecryptorInstance) advanceState(seqNo uint64) (int, *bit.Set) {
10×
262
        var (
10×
263
                offset = int(seqNo & ((1 << WindowSize) - 1))
10×
264
                window = seqNo >> WindowSize
10×
265
        )
10×
266
        switch delta := int64(window - di.currentWindow); {
10×
267
        case delta < -1:
!
268
                return offset, nil
!
269
        case delta == -1:
!
270
                return offset, di.previousUsedOffsets
!
271
        default:
10×
272
                return offset, di.usedOffsets
10×
273
        case delta == +1:
!
274
                di.currentWindow = window
!
275
                di.previousUsedOffsets = di.usedOffsets
!
276
                di.usedOffsets = bit.New()
!
277
                return offset, di.usedOffsets
!
278
        case delta > +1:
!
279
                di.currentWindow = window
!
280
                di.previousUsedOffsets = bit.New()
!
281
                di.usedOffsets = bit.New()
!
282
                return offset, di.usedOffsets
!
283
        }
284
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc