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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

87.42
/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.
815
                case start > macSize && end > macSize:
2✔
816
                        nn = n
2✔
817

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

822
                if err != nil {
5✔
823
                        return nn, err
2✔
824
                }
2✔
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