• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

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

86.57
/brontide/noise.go
1
package brontide
2

3
import (
4
        "crypto/cipher"
5
        "crypto/sha256"
6
        "encoding/binary"
7
        "errors"
8
        "fmt"
9
        "io"
10
        "math"
11
        "time"
12

13
        "github.com/btcsuite/btcd/btcec/v2"
14
        "github.com/lightningnetwork/lnd/keychain"
15
        "golang.org/x/crypto/chacha20poly1305"
16
        "golang.org/x/crypto/hkdf"
17
)
18

19
const (
20
        // protocolName is the precise instantiation of the Noise protocol
21
        // handshake at the center of Brontide. This value will be used as part
22
        // of the prologue. If the initiator and responder aren't using the
23
        // exact same string for this value, along with prologue of the Bitcoin
24
        // network, then the initial handshake will fail.
25
        protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256"
26

27
        // macSize is the length in bytes of the tags generated by poly1305.
28
        macSize = 16
29

30
        // lengthHeaderSize is the number of bytes used to prefix encode the
31
        // length of a message payload.
32
        lengthHeaderSize = 2
33

34
        // encHeaderSize is the number of bytes required to hold an encrypted
35
        // header and it's MAC.
36
        encHeaderSize = lengthHeaderSize + macSize
37

38
        // keyRotationInterval is the number of messages sent on a single
39
        // cipher stream before the keys are rotated forwards.
40
        keyRotationInterval = 1000
41

42
        // handshakeReadTimeout is a read timeout that will be enforced when
43
        // waiting for data payloads during the various acts of Brontide. If
44
        // the remote party fails to deliver the proper payload within this
45
        // time frame, then we'll fail the connection.
46
        handshakeReadTimeout = time.Second * 5
47
)
48

49
var (
50
        // ErrMaxMessageLengthExceeded is returned when a message to be written to
51
        // the cipher session exceeds the maximum allowed message payload.
52
        ErrMaxMessageLengthExceeded = errors.New("the generated payload exceeds " +
53
                "the max allowed message length of (2^16)-1")
54

55
        // ErrMessageNotFlushed signals that the connection cannot accept a new
56
        // message because the prior message has not been fully flushed.
57
        ErrMessageNotFlushed = errors.New("prior message not flushed")
58

59
        // lightningPrologue is the noise prologue that is used to initialize
60
        // the brontide noise handshake.
61
        lightningPrologue = []byte("lightning")
62

63
        // ephemeralGen is the default ephemeral key generator, used to derive a
64
        // unique ephemeral key for each brontide handshake.
65
        ephemeralGen = func() (*btcec.PrivateKey, error) {
3✔
66
                return btcec.NewPrivateKey()
3✔
67
        }
3✔
68
)
69

70
// TODO(roasbeef): free buffer pool?
71

72
// ecdh performs an ECDH operation between pub and priv. The returned value is
73
// the sha256 of the compressed shared point.
74
func ecdh(pub *btcec.PublicKey, priv keychain.SingleKeyECDH) ([]byte, error) {
3✔
75
        hash, err := priv.ECDH(pub)
3✔
76
        return hash[:], err
3✔
77
}
3✔
78

79
// cipherState encapsulates the state for the AEAD which will be used to
80
// encrypt+authenticate any payloads sent during the handshake, and messages
81
// sent once the handshake has completed.
82
type cipherState struct {
83
        // nonce is the nonce passed into the chacha20-poly1305 instance for
84
        // encryption+decryption. The nonce is incremented after each successful
85
        // encryption/decryption.
86
        //
87
        // TODO(roasbeef): this should actually be 96 bit
88
        nonce uint64
89

90
        // secretKey is the shared symmetric key which will be used to
91
        // instantiate the cipher.
92
        //
93
        // TODO(roasbeef): m-lock??
94
        secretKey [32]byte
95

96
        // salt is an additional secret which is used during key rotation to
97
        // generate new keys.
98
        salt [32]byte
99

100
        // cipher is an instance of the ChaCha20-Poly1305 AEAD construction
101
        // created using the secretKey above.
102
        cipher cipher.AEAD
103
}
104

105
// Encrypt returns a ciphertext which is the encryption of the plainText
106
// observing the passed associatedData within the AEAD construction.
107
func (c *cipherState) Encrypt(associatedData, cipherText, plainText []byte) []byte {
3✔
108
        defer func() {
6✔
109
                c.nonce++
3✔
110

3✔
111
                if c.nonce == keyRotationInterval {
6✔
112
                        c.rotateKey()
3✔
113
                }
3✔
114
        }()
115

116
        var nonce [12]byte
3✔
117
        binary.LittleEndian.PutUint64(nonce[4:], c.nonce)
3✔
118

3✔
119
        return c.cipher.Seal(cipherText, nonce[:], plainText, associatedData)
3✔
120
}
121

122
// Decrypt attempts to decrypt the passed ciphertext observing the specified
123
// associatedData within the AEAD construction. In the case that the final MAC
124
// check fails, then a non-nil error will be returned.
125
func (c *cipherState) Decrypt(associatedData, plainText, cipherText []byte) ([]byte, error) {
3✔
126
        defer func() {
6✔
127
                c.nonce++
3✔
128

3✔
129
                if c.nonce == keyRotationInterval {
6✔
130
                        c.rotateKey()
3✔
131
                }
3✔
132
        }()
133

134
        var nonce [12]byte
3✔
135
        binary.LittleEndian.PutUint64(nonce[4:], c.nonce)
3✔
136

3✔
137
        return c.cipher.Open(plainText, nonce[:], cipherText, associatedData)
3✔
138
}
139

140
// InitializeKey initializes the secret key and AEAD cipher scheme based off of
141
// the passed key.
142
func (c *cipherState) InitializeKey(key [32]byte) {
3✔
143
        c.secretKey = key
3✔
144
        c.nonce = 0
3✔
145

3✔
146
        // Safe to ignore the error here as our key is properly sized
3✔
147
        // (32-bytes).
3✔
148
        c.cipher, _ = chacha20poly1305.New(c.secretKey[:])
3✔
149
}
3✔
150

151
// InitializeKeyWithSalt is identical to InitializeKey however it also sets the
152
// cipherState's salt field which is used for key rotation.
153
func (c *cipherState) InitializeKeyWithSalt(salt, key [32]byte) {
3✔
154
        c.salt = salt
3✔
155
        c.InitializeKey(key)
3✔
156
}
3✔
157

158
// rotateKey rotates the current encryption/decryption key for this cipherState
159
// instance. Key rotation is performed by ratcheting the current key forward
160
// using an HKDF invocation with the cipherState's salt as the salt, and the
161
// current key as the input.
162
func (c *cipherState) rotateKey() {
3✔
163
        var (
3✔
164
                info    []byte
3✔
165
                nextKey [32]byte
3✔
166
        )
3✔
167

3✔
168
        oldKey := c.secretKey
3✔
169
        h := hkdf.New(sha256.New, oldKey[:], c.salt[:], info)
3✔
170

3✔
171
        // hkdf(ck, k, zero)
3✔
172
        // |
3✔
173
        // | \
3✔
174
        // |  \
3✔
175
        // ck  k'
3✔
176
        h.Read(c.salt[:])
3✔
177
        h.Read(nextKey[:])
3✔
178

3✔
179
        c.InitializeKey(nextKey)
3✔
180
}
3✔
181

182
// symmetricState encapsulates a cipherState object and houses the ephemeral
183
// handshake digest state. This struct is used during the handshake to derive
184
// new shared secrets based off of the result of ECDH operations. Ultimately,
185
// the final key yielded by this struct is the result of an incremental
186
// Triple-DH operation.
187
type symmetricState struct {
188
        cipherState
189

190
        // chainingKey is used as the salt to the HKDF function to derive a new
191
        // chaining key as well as a new tempKey which is used for
192
        // encryption/decryption.
193
        chainingKey [32]byte
194

195
        // tempKey is the latter 32 bytes resulted from the latest HKDF
196
        // iteration. This key is used to encrypt/decrypt any handshake
197
        // messages or payloads sent until the next DH operation is executed.
198
        tempKey [32]byte
199

200
        // handshakeDigest is the cumulative hash digest of all handshake
201
        // messages sent from start to finish. This value is never transmitted
202
        // to the other side, but will be used as the AD when
203
        // encrypting/decrypting messages using our AEAD construction.
204
        handshakeDigest [32]byte
205
}
206

207
// mixKey implements a basic HKDF-based key ratchet. This method is called
208
// with the result of each DH output generated during the handshake process.
209
// The first 32 bytes extract from the HKDF reader is the next chaining key,
210
// then latter 32 bytes become the temp secret key using within any future AEAD
211
// operations until another DH operation is performed.
212
func (s *symmetricState) mixKey(input []byte) {
3✔
213
        var info []byte
3✔
214

3✔
215
        secret := input
3✔
216
        salt := s.chainingKey
3✔
217
        h := hkdf.New(sha256.New, secret, salt[:], info)
3✔
218

3✔
219
        // hkdf(ck, input, zero)
3✔
220
        // |
3✔
221
        // | \
3✔
222
        // |  \
3✔
223
        // ck  k
3✔
224
        h.Read(s.chainingKey[:])
3✔
225
        h.Read(s.tempKey[:])
3✔
226

3✔
227
        // cipher.k = temp_key
3✔
228
        s.InitializeKey(s.tempKey)
3✔
229
}
3✔
230

231
// mixHash hashes the passed input data into the cumulative handshake digest.
232
// The running result of this value (h) is used as the associated data in all
233
// decryption/encryption operations.
234
func (s *symmetricState) mixHash(data []byte) {
3✔
235
        h := sha256.New()
3✔
236
        h.Write(s.handshakeDigest[:])
3✔
237
        h.Write(data)
3✔
238

3✔
239
        copy(s.handshakeDigest[:], h.Sum(nil))
3✔
240
}
3✔
241

242
// EncryptAndHash returns the authenticated encryption of the passed plaintext.
243
// When encrypting the handshake digest (h) is used as the associated data to
244
// the AEAD cipher.
245
func (s *symmetricState) EncryptAndHash(plaintext []byte) []byte {
3✔
246
        ciphertext := s.Encrypt(s.handshakeDigest[:], nil, plaintext)
3✔
247

3✔
248
        s.mixHash(ciphertext)
3✔
249

3✔
250
        return ciphertext
3✔
251
}
3✔
252

253
// DecryptAndHash returns the authenticated decryption of the passed
254
// ciphertext. When encrypting the handshake digest (h) is used as the
255
// associated data to the AEAD cipher.
256
func (s *symmetricState) DecryptAndHash(ciphertext []byte) ([]byte, error) {
3✔
257
        plaintext, err := s.Decrypt(s.handshakeDigest[:], nil, ciphertext)
3✔
258
        if err != nil {
3✔
UNCOV
259
                return nil, err
×
UNCOV
260
        }
×
261

262
        s.mixHash(ciphertext)
3✔
263

3✔
264
        return plaintext, nil
3✔
265
}
266

267
// InitializeSymmetric initializes the symmetric state by setting the handshake
268
// digest (h) and the chaining key (ck) to protocol name.
269
func (s *symmetricState) InitializeSymmetric(protocolName []byte) {
3✔
270
        var empty [32]byte
3✔
271

3✔
272
        s.handshakeDigest = sha256.Sum256(protocolName)
3✔
273
        s.chainingKey = s.handshakeDigest
3✔
274
        s.InitializeKey(empty)
3✔
275
}
3✔
276

277
// handshakeState encapsulates the symmetricState and keeps track of all the
278
// public keys (static and ephemeral) for both sides during the handshake
279
// transcript. If the handshake completes successfully, then two instances of a
280
// cipherState are emitted: one to encrypt messages from initiator to
281
// responder, and the other for the opposite direction.
282
type handshakeState struct {
283
        symmetricState
284

285
        initiator bool
286

287
        localStatic    keychain.SingleKeyECDH
288
        localEphemeral keychain.SingleKeyECDH // nolint (false positive)
289

290
        remoteStatic    *btcec.PublicKey
291
        remoteEphemeral *btcec.PublicKey
292
}
293

294
// newHandshakeState returns a new instance of the handshake state initialized
295
// with the prologue and protocol name. If this is the responder's handshake
296
// state, then the remotePub can be nil.
297
func newHandshakeState(initiator bool, prologue []byte,
298
        localKey keychain.SingleKeyECDH,
299
        remotePub *btcec.PublicKey) handshakeState {
3✔
300

3✔
301
        h := handshakeState{
3✔
302
                initiator:    initiator,
3✔
303
                localStatic:  localKey,
3✔
304
                remoteStatic: remotePub,
3✔
305
        }
3✔
306

3✔
307
        // Set the current chaining key and handshake digest to the hash of the
3✔
308
        // protocol name, and additionally mix in the prologue. If either sides
3✔
309
        // disagree about the prologue or protocol name, then the handshake
3✔
310
        // will fail.
3✔
311
        h.InitializeSymmetric([]byte(protocolName))
3✔
312
        h.mixHash(prologue)
3✔
313

3✔
314
        // In Noise_XK, the initiator should know the responder's static
3✔
315
        // public key, therefore we include the responder's static key in the
3✔
316
        // handshake digest. If the initiator gets this value wrong, then the
3✔
317
        // handshake will fail.
3✔
318
        if initiator {
6✔
319
                h.mixHash(remotePub.SerializeCompressed())
3✔
320
        } else {
6✔
321
                h.mixHash(localKey.PubKey().SerializeCompressed())
3✔
322
        }
3✔
323

324
        return h
3✔
325
}
326

327
// EphemeralGenerator is a functional option that allows callers to substitute
328
// a custom function for use when generating ephemeral keys for ActOne or
329
// ActTwo. The function closure returned by this function can be passed into
330
// NewBrontideMachine as a function option parameter.
UNCOV
331
func EphemeralGenerator(gen func() (*btcec.PrivateKey, error)) func(*Machine) {
×
UNCOV
332
        return func(m *Machine) {
×
UNCOV
333
                m.ephemeralGen = gen
×
UNCOV
334
        }
×
335
}
336

337
// Machine is a state-machine which implements Brontide: an
338
// Authenticated-key Exchange in Three Acts. Brontide is derived from the Noise
339
// framework, specifically implementing the Noise_XK handshake. Once the
340
// initial 3-act handshake has completed all messages are encrypted with a
341
// chacha20 AEAD cipher. On the wire, all messages are prefixed with an
342
// authenticated+encrypted length field. Additionally, the encrypted+auth'd
343
// length prefix is used as the AD when encrypting+decryption messages. This
344
// construction provides confidentiality of packet length, avoids introducing
345
// a padding-oracle, and binds the encrypted packet length to the packet
346
// itself.
347
//
348
// The acts proceeds the following order (initiator on the left):
349
//
350
//        GenActOne()   ->
351
//                          RecvActOne()
352
//                      <-  GenActTwo()
353
//        RecvActTwo()
354
//        GenActThree() ->
355
//                          RecvActThree()
356
//
357
// This exchange corresponds to the following Noise handshake:
358
//
359
//        <- s
360
//        ...
361
//        -> e, es
362
//        <- e, ee
363
//        -> s, se
364
type Machine struct {
365
        sendCipher cipherState
366
        recvCipher cipherState
367

368
        ephemeralGen func() (*btcec.PrivateKey, error)
369

370
        handshakeState
371

372
        // nextCipherHeader is a static buffer that we'll use to read in the
373
        // next ciphertext header from the wire. The header is a 2 byte length
374
        // (of the next ciphertext), followed by a 16 byte MAC.
375
        nextCipherHeader [encHeaderSize]byte
376

377
        // nextHeaderSend holds a reference to the remaining header bytes to
378
        // write out for a pending message. This allows us to tolerate timeout
379
        // errors that cause partial writes.
380
        nextHeaderSend []byte
381

382
        // nextBodySend holds a reference to the remaining body bytes to write
383
        // out for a pending message. This allows us to tolerate timeout errors
384
        // that cause partial writes.
385
        nextBodySend []byte
386
}
387

388
// NewBrontideMachine creates a new instance of the brontide state-machine. If
389
// the responder (listener) is creating the object, then the remotePub should
390
// be nil. The handshake state within brontide is initialized using the ascii
391
// string "lightning" as the prologue. The last parameter is a set of variadic
392
// arguments for adding additional options to the brontide Machine
393
// initialization.
394
func NewBrontideMachine(initiator bool, localKey keychain.SingleKeyECDH,
395
        remotePub *btcec.PublicKey, options ...func(*Machine)) *Machine {
3✔
396

3✔
397
        handshake := newHandshakeState(
3✔
398
                initiator, lightningPrologue, localKey, remotePub,
3✔
399
        )
3✔
400

3✔
401
        m := &Machine{
3✔
402
                handshakeState: handshake,
3✔
403
                ephemeralGen:   ephemeralGen,
3✔
404
        }
3✔
405

3✔
406
        // With the default options established, we'll now process all the
3✔
407
        // options passed in as parameters.
3✔
408
        for _, option := range options {
3✔
UNCOV
409
                option(m)
×
UNCOV
410
        }
×
411

412
        return m
3✔
413
}
414

415
const (
416
        // HandshakeVersion is the expected version of the brontide handshake.
417
        // Any messages that carry a different version will cause the handshake
418
        // to abort immediately.
419
        HandshakeVersion = byte(0)
420

421
        // ActOneSize is the size of the packet sent from initiator to
422
        // responder in ActOne. The packet consists of a handshake version, an
423
        // ephemeral key in compressed format, and a 16-byte poly1305 tag.
424
        //
425
        // 1 + 33 + 16
426
        ActOneSize = 50
427

428
        // ActTwoSize is the size the packet sent from responder to initiator
429
        // in ActTwo. The packet consists of a handshake version, an ephemeral
430
        // key in compressed format and a 16-byte poly1305 tag.
431
        //
432
        // 1 + 33 + 16
433
        ActTwoSize = 50
434

435
        // ActThreeSize is the size of the packet sent from initiator to
436
        // responder in ActThree. The packet consists of a handshake version,
437
        // the initiators static key encrypted with strong forward secrecy and
438
        // a 16-byte poly1035 tag.
439
        //
440
        // 1 + 33 + 16 + 16
441
        ActThreeSize = 66
442
)
443

444
// GenActOne generates the initial packet (act one) to be sent from initiator
445
// to responder. During act one the initiator generates a fresh ephemeral key,
446
// hashes it into the handshake digest, and performs an ECDH between this key
447
// and the responder's static key. Future payloads are encrypted with a key
448
// derived from this result.
449
//
450
//        -> e, es
451
func (b *Machine) GenActOne() ([ActOneSize]byte, error) {
3✔
452
        var actOne [ActOneSize]byte
3✔
453

3✔
454
        // e
3✔
455
        localEphemeral, err := b.ephemeralGen()
3✔
456
        if err != nil {
3✔
457
                return actOne, err
×
458
        }
×
459
        b.localEphemeral = &keychain.PrivKeyECDH{
3✔
460
                PrivKey: localEphemeral,
3✔
461
        }
3✔
462

3✔
463
        ephemeral := localEphemeral.PubKey().SerializeCompressed()
3✔
464
        b.mixHash(ephemeral)
3✔
465

3✔
466
        // es
3✔
467
        s, err := ecdh(b.remoteStatic, b.localEphemeral)
3✔
468
        if err != nil {
3✔
469
                return actOne, err
×
470
        }
×
471
        b.mixKey(s[:])
3✔
472

3✔
473
        authPayload := b.EncryptAndHash([]byte{})
3✔
474

3✔
475
        actOne[0] = HandshakeVersion
3✔
476
        copy(actOne[1:34], ephemeral)
3✔
477
        copy(actOne[34:], authPayload)
3✔
478

3✔
479
        return actOne, nil
3✔
480
}
481

482
// RecvActOne processes the act one packet sent by the initiator. The responder
483
// executes the mirrored actions to that of the initiator extending the
484
// handshake digest and deriving a new shared secret based on an ECDH with the
485
// initiator's ephemeral key and responder's static key.
486
func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error {
3✔
487
        var (
3✔
488
                err error
3✔
489
                e   [33]byte
3✔
490
                p   [16]byte
3✔
491
        )
3✔
492

3✔
493
        // If the handshake version is unknown, then the handshake fails
3✔
494
        // immediately.
3✔
495
        if actOne[0] != HandshakeVersion {
3✔
UNCOV
496
                return fmt.Errorf("act one: invalid handshake version: %v, "+
×
UNCOV
497
                        "only %v is valid, msg=%x", actOne[0], HandshakeVersion,
×
UNCOV
498
                        actOne[:])
×
UNCOV
499
        }
×
500

501
        copy(e[:], actOne[1:34])
3✔
502
        copy(p[:], actOne[34:])
3✔
503

3✔
504
        // e
3✔
505
        b.remoteEphemeral, err = btcec.ParsePubKey(e[:])
3✔
506
        if err != nil {
3✔
UNCOV
507
                return err
×
UNCOV
508
        }
×
509
        b.mixHash(b.remoteEphemeral.SerializeCompressed())
3✔
510

3✔
511
        // es
3✔
512
        s, err := ecdh(b.remoteEphemeral, b.localStatic)
3✔
513
        if err != nil {
3✔
514
                return err
×
515
        }
×
516
        b.mixKey(s)
3✔
517

3✔
518
        // If the initiator doesn't know our static key, then this operation
3✔
519
        // will fail.
3✔
520
        _, err = b.DecryptAndHash(p[:])
3✔
521
        return err
3✔
522
}
523

524
// GenActTwo generates the second packet (act two) to be sent from the
525
// responder to the initiator. The packet for act two is identical to that of
526
// act one, but then results in a different ECDH operation between the
527
// initiator's and responder's ephemeral keys.
528
//
529
//        <- e, ee
530
func (b *Machine) GenActTwo() ([ActTwoSize]byte, error) {
3✔
531
        var actTwo [ActTwoSize]byte
3✔
532

3✔
533
        // e
3✔
534
        localEphemeral, err := b.ephemeralGen()
3✔
535
        if err != nil {
3✔
536
                return actTwo, err
×
537
        }
×
538
        b.localEphemeral = &keychain.PrivKeyECDH{
3✔
539
                PrivKey: localEphemeral,
3✔
540
        }
3✔
541

3✔
542
        ephemeral := localEphemeral.PubKey().SerializeCompressed()
3✔
543
        b.mixHash(localEphemeral.PubKey().SerializeCompressed())
3✔
544

3✔
545
        // ee
3✔
546
        s, err := ecdh(b.remoteEphemeral, b.localEphemeral)
3✔
547
        if err != nil {
3✔
548
                return actTwo, err
×
549
        }
×
550
        b.mixKey(s)
3✔
551

3✔
552
        authPayload := b.EncryptAndHash([]byte{})
3✔
553

3✔
554
        actTwo[0] = HandshakeVersion
3✔
555
        copy(actTwo[1:34], ephemeral)
3✔
556
        copy(actTwo[34:], authPayload)
3✔
557

3✔
558
        return actTwo, nil
3✔
559
}
560

561
// RecvActTwo processes the second packet (act two) sent from the responder to
562
// the initiator. A successful processing of this packet authenticates the
563
// initiator to the responder.
564
func (b *Machine) RecvActTwo(actTwo [ActTwoSize]byte) error {
3✔
565
        var (
3✔
566
                err error
3✔
567
                e   [33]byte
3✔
568
                p   [16]byte
3✔
569
        )
3✔
570

3✔
571
        // If the handshake version is unknown, then the handshake fails
3✔
572
        // immediately.
3✔
573
        if actTwo[0] != HandshakeVersion {
3✔
UNCOV
574
                return fmt.Errorf("act two: invalid handshake version: %v, "+
×
UNCOV
575
                        "only %v is valid, msg=%x", actTwo[0], HandshakeVersion,
×
UNCOV
576
                        actTwo[:])
×
UNCOV
577
        }
×
578

579
        copy(e[:], actTwo[1:34])
3✔
580
        copy(p[:], actTwo[34:])
3✔
581

3✔
582
        // e
3✔
583
        b.remoteEphemeral, err = btcec.ParsePubKey(e[:])
3✔
584
        if err != nil {
3✔
UNCOV
585
                return err
×
UNCOV
586
        }
×
587
        b.mixHash(b.remoteEphemeral.SerializeCompressed())
3✔
588

3✔
589
        // ee
3✔
590
        s, err := ecdh(b.remoteEphemeral, b.localEphemeral)
3✔
591
        if err != nil {
3✔
592
                return err
×
593
        }
×
594
        b.mixKey(s)
3✔
595

3✔
596
        _, err = b.DecryptAndHash(p[:])
3✔
597
        return err
3✔
598
}
599

600
// GenActThree creates the final (act three) packet of the handshake. Act three
601
// is to be sent from the initiator to the responder. The purpose of act three
602
// is to transmit the initiator's public key under strong forward secrecy to
603
// the responder. This act also includes the final ECDH operation which yields
604
// the final session.
605
//
606
//        -> s, se
607
func (b *Machine) GenActThree() ([ActThreeSize]byte, error) {
3✔
608
        var actThree [ActThreeSize]byte
3✔
609

3✔
610
        ourPubkey := b.localStatic.PubKey().SerializeCompressed()
3✔
611
        ciphertext := b.EncryptAndHash(ourPubkey)
3✔
612

3✔
613
        s, err := ecdh(b.remoteEphemeral, b.localStatic)
3✔
614
        if err != nil {
3✔
615
                return actThree, err
×
616
        }
×
617
        b.mixKey(s)
3✔
618

3✔
619
        authPayload := b.EncryptAndHash([]byte{})
3✔
620

3✔
621
        actThree[0] = HandshakeVersion
3✔
622
        copy(actThree[1:50], ciphertext)
3✔
623
        copy(actThree[50:], authPayload)
3✔
624

3✔
625
        // With the final ECDH operation complete, derive the session sending
3✔
626
        // and receiving keys.
3✔
627
        b.split()
3✔
628

3✔
629
        return actThree, nil
3✔
630
}
631

632
// RecvActThree processes the final act (act three) sent from the initiator to
633
// the responder. After processing this act, the responder learns of the
634
// initiator's static public key. Decryption of the static key serves to
635
// authenticate the initiator to the responder.
636
func (b *Machine) RecvActThree(actThree [ActThreeSize]byte) error {
3✔
637
        var (
3✔
638
                err error
3✔
639
                s   [33 + 16]byte
3✔
640
                p   [16]byte
3✔
641
        )
3✔
642

3✔
643
        // If the handshake version is unknown, then the handshake fails
3✔
644
        // immediately.
3✔
645
        if actThree[0] != HandshakeVersion {
3✔
UNCOV
646
                return fmt.Errorf("act three: invalid handshake version: %v, "+
×
UNCOV
647
                        "only %v is valid, msg=%x", actThree[0], HandshakeVersion,
×
UNCOV
648
                        actThree[:])
×
UNCOV
649
        }
×
650

651
        copy(s[:], actThree[1:33+16+1])
3✔
652
        copy(p[:], actThree[33+16+1:])
3✔
653

3✔
654
        // s
3✔
655
        remotePub, err := b.DecryptAndHash(s[:])
3✔
656
        if err != nil {
3✔
UNCOV
657
                return err
×
UNCOV
658
        }
×
659
        b.remoteStatic, err = btcec.ParsePubKey(remotePub)
3✔
660
        if err != nil {
3✔
661
                return err
×
662
        }
×
663

664
        // se
665
        se, err := ecdh(b.remoteStatic, b.localEphemeral)
3✔
666
        if err != nil {
3✔
667
                return err
×
668
        }
×
669
        b.mixKey(se)
3✔
670

3✔
671
        if _, err := b.DecryptAndHash(p[:]); err != nil {
3✔
672
                return err
×
673
        }
×
674

675
        // With the final ECDH operation complete, derive the session sending
676
        // and receiving keys.
677
        b.split()
3✔
678

3✔
679
        return nil
3✔
680
}
681

682
// split is the final wrap-up act to be executed at the end of a successful
683
// three act handshake. This function creates two internal cipherState
684
// instances: one which is used to encrypt messages from the initiator to the
685
// responder, and another which is used to encrypt message for the opposite
686
// direction.
687
func (b *Machine) split() {
3✔
688
        var (
3✔
689
                empty   []byte
3✔
690
                sendKey [32]byte
3✔
691
                recvKey [32]byte
3✔
692
        )
3✔
693

3✔
694
        h := hkdf.New(sha256.New, empty, b.chainingKey[:], empty)
3✔
695

3✔
696
        // If we're the initiator the first 32 bytes are used to encrypt our
3✔
697
        // messages and the second 32-bytes to decrypt their messages. For the
3✔
698
        // responder the opposite is true.
3✔
699
        if b.initiator {
6✔
700
                h.Read(sendKey[:])
3✔
701
                b.sendCipher = cipherState{}
3✔
702
                b.sendCipher.InitializeKeyWithSalt(b.chainingKey, sendKey)
3✔
703

3✔
704
                h.Read(recvKey[:])
3✔
705
                b.recvCipher = cipherState{}
3✔
706
                b.recvCipher.InitializeKeyWithSalt(b.chainingKey, recvKey)
3✔
707
        } else {
6✔
708
                h.Read(recvKey[:])
3✔
709
                b.recvCipher = cipherState{}
3✔
710
                b.recvCipher.InitializeKeyWithSalt(b.chainingKey, recvKey)
3✔
711

3✔
712
                h.Read(sendKey[:])
3✔
713
                b.sendCipher = cipherState{}
3✔
714
                b.sendCipher.InitializeKeyWithSalt(b.chainingKey, sendKey)
3✔
715
        }
3✔
716
}
717

718
// WriteMessage encrypts and buffers the next message p. The ciphertext of the
719
// message is prepended with an encrypt+auth'd length which must be used as the
720
// AD to the AEAD construction when being decrypted by the other side.
721
//
722
// NOTE: This DOES NOT write the message to the wire, it should be followed by a
723
// call to Flush to ensure the message is written.
724
func (b *Machine) WriteMessage(p []byte) error {
3✔
725
        // The total length of each message payload including the MAC size
3✔
726
        // payload exceed the largest number encodable within a 16-bit unsigned
3✔
727
        // integer.
3✔
728
        if len(p) > math.MaxUint16 {
3✔
UNCOV
729
                return ErrMaxMessageLengthExceeded
×
UNCOV
730
        }
×
731

732
        // If a prior message was written but it hasn't been fully flushed,
733
        // return an error as we only support buffering of one message at a
734
        // time.
735
        if len(b.nextHeaderSend) > 0 || len(b.nextBodySend) > 0 {
3✔
736
                return ErrMessageNotFlushed
×
737
        }
×
738

739
        // The full length of the packet is only the packet length, and does
740
        // NOT include the MAC.
741
        fullLength := uint16(len(p))
3✔
742

3✔
743
        var pktLen [2]byte
3✔
744
        binary.BigEndian.PutUint16(pktLen[:], fullLength)
3✔
745

3✔
746
        // First, generate the encrypted+MAC'd length prefix for the packet.
3✔
747
        b.nextHeaderSend = b.sendCipher.Encrypt(nil, nil, pktLen[:])
3✔
748

3✔
749
        // Finally, generate the encrypted packet itself.
3✔
750
        b.nextBodySend = b.sendCipher.Encrypt(nil, nil, p)
3✔
751

3✔
752
        return nil
3✔
753
}
754

755
// Flush attempts to write a message buffered using WriteMessage to the provided
756
// io.Writer. If no buffered message exists, this will result in a NOP.
757
// Otherwise, it will continue to write the remaining bytes, picking up where
758
// the byte stream left off in the event of a partial write. The number of bytes
759
// returned reflects the number of plaintext bytes in the payload, and does not
760
// account for the overhead of the header or MACs.
761
//
762
// NOTE: It is safe to call this method again iff a timeout error is returned.
763
func (b *Machine) Flush(w io.Writer) (int, error) {
3✔
764
        // First, write out the pending header bytes, if any exist. Any header
3✔
765
        // bytes written will not count towards the total amount flushed.
3✔
766
        if len(b.nextHeaderSend) > 0 {
6✔
767
                // Write any remaining header bytes and shift the slice to point
3✔
768
                // to the next segment of unwritten bytes. If an error is
3✔
769
                // encountered, we can continue to write the header from where
3✔
770
                // we left off on a subsequent call to Flush.
3✔
771
                n, err := w.Write(b.nextHeaderSend)
3✔
772
                b.nextHeaderSend = b.nextHeaderSend[n:]
3✔
773
                if err != nil {
3✔
UNCOV
774
                        return 0, err
×
UNCOV
775
                }
×
776
        }
777

778
        // Next, write the pending body bytes, if any exist. Only the number of
779
        // bytes written that correspond to the ciphertext will be included in
780
        // the total bytes written, bytes written as part of the MAC will not be
781
        // counted.
782
        var nn int
3✔
783
        if len(b.nextBodySend) > 0 {
6✔
784
                // Write out all bytes excluding the mac and shift the body
3✔
785
                // slice depending on the number of actual bytes written.
3✔
786
                n, err := w.Write(b.nextBodySend)
3✔
787
                b.nextBodySend = b.nextBodySend[n:]
3✔
788

3✔
789
                // If we partially or fully wrote any of the body's MAC, we'll
3✔
790
                // subtract that contribution from the total amount flushed to
3✔
791
                // preserve the abstraction of returning the number of plaintext
3✔
792
                // bytes written by the connection.
3✔
793
                //
3✔
794
                // There are three possible scenarios we must handle to ensure
3✔
795
                // the returned value is correct. In the first case, the write
3✔
796
                // straddles both payload and MAC bytes, and we must subtract
3✔
797
                // the number of MAC bytes written from n. In the second, only
3✔
798
                // payload bytes are written, thus we can return n unmodified.
3✔
799
                // The final scenario pertains to the case where only MAC bytes
3✔
800
                // are written, none of which count towards the total.
3✔
801
                //
3✔
802
                //                 |-----------Payload------------|----MAC----|
3✔
803
                // Straddle:       S---------------------------------E--------0
3✔
804
                // Payload-only:   S------------------------E-----------------0
3✔
805
                // MAC-only:                                        S-------E-0
3✔
806
                start, end := n+len(b.nextBodySend), len(b.nextBodySend)
3✔
807
                switch {
3✔
808

809
                // Straddles payload and MAC bytes, subtract number of MAC bytes
810
                // written from the actual number written.
811
                case start > macSize && end <= macSize:
3✔
812
                        nn = n - (macSize - end)
3✔
813

814
                // Only payload bytes are written, return n directly.
UNCOV
815
                case start > macSize && end > macSize:
×
UNCOV
816
                        nn = n
×
817

818
                // Only MAC bytes are written, return 0 bytes written.
UNCOV
819
                default:
×
820
                }
821

822
                if err != nil {
3✔
UNCOV
823
                        return nn, err
×
UNCOV
824
                }
×
825
        }
826

827
        return nn, nil
3✔
828
}
829

830
// ReadMessage attempts to read the next message from the passed io.Reader. In
831
// the case of an authentication error, a non-nil error is returned.
832
func (b *Machine) ReadMessage(r io.Reader) ([]byte, error) {
3✔
833
        pktLen, err := b.ReadHeader(r)
3✔
834
        if err != nil {
3✔
UNCOV
835
                return nil, err
×
UNCOV
836
        }
×
837

838
        buf := make([]byte, pktLen)
3✔
839
        return b.ReadBody(r, buf)
3✔
840
}
841

842
// ReadHeader attempts to read the next message header from the passed
843
// io.Reader. The header contains the length of the next body including
844
// additional overhead of the MAC. In the case of an authentication error, a
845
// non-nil error is returned.
846
//
847
// NOTE: This method SHOULD NOT be used in the case that the io.Reader may be
848
// adversarial and induce long delays. If the caller needs to set read deadlines
849
// appropriately, it is preferred that they use the split ReadHeader and
850
// ReadBody methods so that the deadlines can be set appropriately on each.
851
func (b *Machine) ReadHeader(r io.Reader) (uint32, error) {
3✔
852
        _, err := io.ReadFull(r, b.nextCipherHeader[:])
3✔
853
        if err != nil {
6✔
854
                return 0, err
3✔
855
        }
3✔
856

857
        // Attempt to decrypt+auth the packet length present in the stream.
858
        //
859
        // By passing in `nextCipherHeader` as the destination, we avoid making
860
        // the library allocate a new buffer to decode the plaintext.
861
        pktLenBytes, err := b.recvCipher.Decrypt(
3✔
862
                nil, b.nextCipherHeader[:0], b.nextCipherHeader[:],
3✔
863
        )
3✔
864
        if err != nil {
3✔
UNCOV
865
                return 0, err
×
UNCOV
866
        }
×
867

868
        // Compute the packet length that we will need to read off the wire.
869
        pktLen := uint32(binary.BigEndian.Uint16(pktLenBytes)) + macSize
3✔
870

3✔
871
        return pktLen, nil
3✔
872
}
873

874
// ReadBody attempts to ready the next message body from the passed io.Reader.
875
// The provided buffer MUST be the length indicated by the packet length
876
// returned by the preceding call to ReadHeader. In the case of an
877
// authentication error, a non-nil error is returned.
878
func (b *Machine) ReadBody(r io.Reader, buf []byte) ([]byte, error) {
3✔
879
        // Next, using the length read from the packet header, read the
3✔
880
        // encrypted packet itself into the buffer allocated by the read
3✔
881
        // pool.
3✔
882
        _, err := io.ReadFull(r, buf)
3✔
883
        if err != nil {
3✔
884
                return nil, err
×
885
        }
×
886

887
        // Finally, decrypt the message held in the buffer, and return a new
888
        // byte slice containing the plaintext.
889
        //
890
        // By passing in the buf (the ciphertext) as the first argument, we end
891
        // up re-using it as we don't force the library to allocate a new
892
        // buffer to decode the plaintext.
893
        return b.recvCipher.Decrypt(nil, buf[:0], buf)
3✔
894
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc