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

lightningnetwork / lnd / 16918135633

12 Aug 2025 06:56PM UTC coverage: 56.955% (-9.9%) from 66.9%
16918135633

push

github

web-flow
Merge pull request #9871 from GeorgeTsagk/htlc-noop-add

Add `NoopAdd` HTLCs

48 of 147 new or added lines in 3 files covered. (32.65%)

29154 existing lines in 462 files now uncovered.

98265 of 172532 relevant lines covered (56.95%)

1.19 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) {
2✔
66
                return btcec.NewPrivateKey()
2✔
67
        }
2✔
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) {
2✔
75
        hash, err := priv.ECDH(pub)
2✔
76
        return hash[:], err
2✔
77
}
2✔
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 {
2✔
108
        defer func() {
4✔
109
                c.nonce++
2✔
110

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

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

2✔
119
        return c.cipher.Seal(cipherText, nonce[:], plainText, associatedData)
2✔
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) {
2✔
126
        defer func() {
4✔
127
                c.nonce++
2✔
128

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

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

2✔
137
        return c.cipher.Open(plainText, nonce[:], cipherText, associatedData)
2✔
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) {
2✔
143
        c.secretKey = key
2✔
144
        c.nonce = 0
2✔
145

2✔
146
        // Safe to ignore the error here as our key is properly sized
2✔
147
        // (32-bytes).
2✔
148
        c.cipher, _ = chacha20poly1305.New(c.secretKey[:])
2✔
149
}
2✔
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) {
2✔
154
        c.salt = salt
2✔
155
        c.InitializeKey(key)
2✔
156
}
2✔
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() {
2✔
163
        var (
2✔
164
                info    []byte
2✔
165
                nextKey [32]byte
2✔
166
        )
2✔
167

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

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

2✔
179
        c.InitializeKey(nextKey)
2✔
180
}
2✔
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) {
2✔
213
        var info []byte
2✔
214

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

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

2✔
227
        // cipher.k = temp_key
2✔
228
        s.InitializeKey(s.tempKey)
2✔
229
}
2✔
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) {
2✔
235
        h := sha256.New()
2✔
236
        h.Write(s.handshakeDigest[:])
2✔
237
        h.Write(data)
2✔
238

2✔
239
        copy(s.handshakeDigest[:], h.Sum(nil))
2✔
240
}
2✔
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 {
2✔
246
        ciphertext := s.Encrypt(s.handshakeDigest[:], nil, plaintext)
2✔
247

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

2✔
250
        return ciphertext
2✔
251
}
2✔
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) {
2✔
257
        plaintext, err := s.Decrypt(s.handshakeDigest[:], nil, ciphertext)
2✔
258
        if err != nil {
2✔
UNCOV
259
                return nil, err
×
UNCOV
260
        }
×
261

262
        s.mixHash(ciphertext)
2✔
263

2✔
264
        return plaintext, nil
2✔
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) {
2✔
270
        var empty [32]byte
2✔
271

2✔
272
        s.handshakeDigest = sha256.Sum256(protocolName)
2✔
273
        s.chainingKey = s.handshakeDigest
2✔
274
        s.InitializeKey(empty)
2✔
275
}
2✔
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 {
2✔
300

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

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

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

324
        return h
2✔
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 {
2✔
396

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

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

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

412
        return m
2✔
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) {
2✔
452
        var actOne [ActOneSize]byte
2✔
453

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

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

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

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

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

2✔
479
        return actOne, nil
2✔
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 {
2✔
487
        var (
2✔
488
                err error
2✔
489
                e   [33]byte
2✔
490
                p   [16]byte
2✔
491
        )
2✔
492

2✔
493
        // If the handshake version is unknown, then the handshake fails
2✔
494
        // immediately.
2✔
495
        if actOne[0] != HandshakeVersion {
2✔
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])
2✔
502
        copy(p[:], actOne[34:])
2✔
503

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

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

2✔
518
        // If the initiator doesn't know our static key, then this operation
2✔
519
        // will fail.
2✔
520
        _, err = b.DecryptAndHash(p[:])
2✔
521
        return err
2✔
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) {
2✔
531
        var actTwo [ActTwoSize]byte
2✔
532

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

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

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

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

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

2✔
558
        return actTwo, nil
2✔
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 {
2✔
565
        var (
2✔
566
                err error
2✔
567
                e   [33]byte
2✔
568
                p   [16]byte
2✔
569
        )
2✔
570

2✔
571
        // If the handshake version is unknown, then the handshake fails
2✔
572
        // immediately.
2✔
573
        if actTwo[0] != HandshakeVersion {
2✔
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])
2✔
580
        copy(p[:], actTwo[34:])
2✔
581

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

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

2✔
596
        _, err = b.DecryptAndHash(p[:])
2✔
597
        return err
2✔
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) {
2✔
608
        var actThree [ActThreeSize]byte
2✔
609

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

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

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

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

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

2✔
629
        return actThree, nil
2✔
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 {
2✔
637
        var (
2✔
638
                err error
2✔
639
                s   [33 + 16]byte
2✔
640
                p   [16]byte
2✔
641
        )
2✔
642

2✔
643
        // If the handshake version is unknown, then the handshake fails
2✔
644
        // immediately.
2✔
645
        if actThree[0] != HandshakeVersion {
2✔
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])
2✔
652
        copy(p[:], actThree[33+16+1:])
2✔
653

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

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

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

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

2✔
679
        return nil
2✔
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() {
2✔
688
        var (
2✔
689
                empty   []byte
2✔
690
                sendKey [32]byte
2✔
691
                recvKey [32]byte
2✔
692
        )
2✔
693

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

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

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

2✔
712
                h.Read(sendKey[:])
2✔
713
                b.sendCipher = cipherState{}
2✔
714
                b.sendCipher.InitializeKeyWithSalt(b.chainingKey, sendKey)
2✔
715
        }
2✔
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 {
2✔
725
        // The total length of each message payload including the MAC size
2✔
726
        // payload exceed the largest number encodable within a 16-bit unsigned
2✔
727
        // integer.
2✔
728
        if len(p) > math.MaxUint16 {
2✔
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 {
2✔
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))
2✔
742

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

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

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

2✔
752
        return nil
2✔
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) {
2✔
764
        // First, write out the pending header bytes, if any exist. Any header
2✔
765
        // bytes written will not count towards the total amount flushed.
2✔
766
        if len(b.nextHeaderSend) > 0 {
4✔
767
                // Write any remaining header bytes and shift the slice to point
2✔
768
                // to the next segment of unwritten bytes. If an error is
2✔
769
                // encountered, we can continue to write the header from where
2✔
770
                // we left off on a subsequent call to Flush.
2✔
771
                n, err := w.Write(b.nextHeaderSend)
2✔
772
                b.nextHeaderSend = b.nextHeaderSend[n:]
2✔
773
                if err != nil {
2✔
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
2✔
783
        if len(b.nextBodySend) > 0 {
4✔
784
                // Write out all bytes excluding the mac and shift the body
2✔
785
                // slice depending on the number of actual bytes written.
2✔
786
                n, err := w.Write(b.nextBodySend)
2✔
787
                b.nextBodySend = b.nextBodySend[n:]
2✔
788

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

827
        return nn, nil
2✔
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) {
2✔
833
        pktLen, err := b.ReadHeader(r)
2✔
834
        if err != nil {
2✔
UNCOV
835
                return nil, err
×
UNCOV
836
        }
×
837

838
        buf := make([]byte, pktLen)
2✔
839
        return b.ReadBody(r, buf)
2✔
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) {
2✔
852
        _, err := io.ReadFull(r, b.nextCipherHeader[:])
2✔
853
        if err != nil {
4✔
854
                return 0, err
2✔
855
        }
2✔
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(
2✔
862
                nil, b.nextCipherHeader[:0], b.nextCipherHeader[:],
2✔
863
        )
2✔
864
        if err != nil {
2✔
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
2✔
870

2✔
871
        return pktLen, nil
2✔
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) {
2✔
879
        // Next, using the length read from the packet header, read the
2✔
880
        // encrypted packet itself into the buffer allocated by the read
2✔
881
        // pool.
2✔
882
        _, err := io.ReadFull(r, buf)
2✔
883
        if err != nil {
2✔
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)
2✔
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