From 1fe156f2f4205f470851863e6b0f53054f39990a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 30 Aug 2025 14:48:15 +0100 Subject: [PATCH] brontide: use a fixed size buffer for the nonce within the brontide machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we use a fixed sized buffer for the nonce when we read/write messages. This was actually escaping to the heap. We can avoid this by statically allocating it alongside the struct itself. The benchmark state at this point: ``` goos: darwin goarch: arm64 pkg: github.com/lightningnetwork/lnd/brontide cpu: Apple M4 Max BenchmarkWriteMessage-16 25264 47012 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 23542 46809 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 25989 47256 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 25542 46388 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 26083 46612 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 25860 46367 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 24967 46748 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 26088 46485 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 25561 46425 ns/op 4 B/op 1 allocs/op BenchmarkWriteMessage-16 25474 47249 ns/op 4 B/op 1 allocs/op PASS ok github.com/lightningnetwork/lnd/brontide 16.911s ``` ``` goos: darwin goarch: arm64 pkg: github.com/lightningnetwork/lnd/brontide cpu: Apple M4 Max │ old.txt │ new2.txt │ │ sec/op │ sec/op vs base │ WriteMessage-16 50.34µ ± 1% 46.68µ ± 1% -7.28% (p=0.000 n=10) │ old.txt │ new2.txt │ │ B/op │ B/op vs base │ WriteMessage-16 73788.000 ± 0% 4.000 ± 0% -99.99% (p=0.000 n=10) │ old.txt │ new2.txt │ │ allocs/op │ allocs/op vs base │ WriteMessage-16 5.000 ± 0% 1.000 ± 0% -80.00% (p=0.000 n=10) ``` --- brontide/noise.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/brontide/noise.go b/brontide/noise.go index 53f951adb..56c4c340b 100644 --- a/brontide/noise.go +++ b/brontide/noise.go @@ -91,6 +91,9 @@ type cipherState struct { // TODO(roasbeef): this should actually be 96 bit nonce uint64 + // nonceBuffer is a reusable buffer for the nonce to avoid allocations. + nonceBuffer [12]byte + // secretKey is the shared symmetric key which will be used to // instantiate the cipher. // @@ -117,10 +120,12 @@ func (c *cipherState) Encrypt(associatedData, cipherText, plainText []byte) []by } }() - var nonce [12]byte - binary.LittleEndian.PutUint64(nonce[4:], c.nonce) + // Write the nonce counter to the buffer (bytes 4-11). + binary.LittleEndian.PutUint64(c.nonceBuffer[4:], c.nonce) - return c.cipher.Seal(cipherText, nonce[:], plainText, associatedData) + return c.cipher.Seal( + cipherText, c.nonceBuffer[:], plainText, associatedData, + ) } // Decrypt attempts to decrypt the passed ciphertext observing the specified @@ -135,10 +140,12 @@ func (c *cipherState) Decrypt(associatedData, plainText, cipherText []byte) ([]b } }() - var nonce [12]byte - binary.LittleEndian.PutUint64(nonce[4:], c.nonce) + // Write the nonce counter to the buffer (bytes 4-11). + binary.LittleEndian.PutUint64(c.nonceBuffer[4:], c.nonce) - return c.cipher.Open(plainText, nonce[:], cipherText, associatedData) + return c.cipher.Open( + plainText, c.nonceBuffer[:], cipherText, associatedData, + ) } // InitializeKey initializes the secret key and AEAD cipher scheme based off of