From: qmuntal Date: Fri, 1 Sep 2023 07:26:56 +0000 (+0200) Subject: crypto/internal/boring: use noescape and nocallback cgo directives X-Git-Tag: go1.22rc1~530 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=e46e8610aba89ed91896ab6ccc5349636e0b4df2;p=gostls13.git crypto/internal/boring: use noescape and nocallback cgo directives The new noescape and nocallback directives can be used instead of the C wrapper functions that are there just to avoid some parameters being escaped to the heap. This CL also helps demonstrate the use of the new directives in real code. I've added some benchmarks to demonstrate that this CL doesn't introduce new heap allocations when using boringcrypto: ``` goos: linux goarch: amd64 pkg: crypto/aes cpu: AMD EPYC 7763 64-Core Processor BenchmarkGCMSeal-32 8378692 143.3 ns/op 111.65 MB/s 0 B/op 0 allocs/op BenchmarkGCMOpen-32 8383038 142.7 ns/op 112.11 MB/s 0 B/op 0 allocs/op ``` Change-Id: Ifd775484eb9a105afc5c3d4e75a6c6655cbadc53 Reviewed-on: https://go-review.googlesource.com/c/go/+/525035 TryBot-Result: Gopher Robot Reviewed-by: Bryan Mills Run-TryBot: Quim Muntal Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI --- diff --git a/src/crypto/cipher/gcm_test.go b/src/crypto/cipher/gcm_test.go index 3556146ea6..7b9d1852d7 100644 --- a/src/crypto/cipher/gcm_test.go +++ b/src/crypto/cipher/gcm_test.go @@ -654,3 +654,39 @@ func TestGCMAsm(t *testing.T) { } } } + +func BenchmarkGCMSeal(b *testing.B) { + key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed") + nonce, _ := hex.DecodeString("54cc7dc2c37ec006bcc6d1db") + plaintext, _ := hex.DecodeString("f1cc3818e421876bb6b8bbd6c9") + + aes, _ := aes.NewCipher(key) + aesgcm, _ := cipher.NewGCM(aes) + + ciphertext := make([]byte, 32) + b.SetBytes(int64(len(plaintext))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = aesgcm.Seal(ciphertext[:0], nonce, plaintext, nil) + } +} + +func BenchmarkGCMOpen(b *testing.B) { + key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed") + nonce, _ := hex.DecodeString("54cc7dc2c37ec006bcc6d1db") + plaintext, _ := hex.DecodeString("f1cc3818e421876bb6b8bbd6c9") + + aes, _ := aes.NewCipher(key) + aesgcm, _ := cipher.NewGCM(aes) + + ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil) + + b.SetBytes(int64(len(ciphertext))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := aesgcm.Open(plaintext[:0], nonce, ciphertext, nil) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/src/crypto/internal/boring/aes.go b/src/crypto/internal/boring/aes.go index 8819f576f4..9520bb0c17 100644 --- a/src/crypto/internal/boring/aes.go +++ b/src/crypto/internal/boring/aes.go @@ -7,40 +7,11 @@ package boring /* - #include "goboringcrypto.h" - -// These wrappers allocate out_len on the C stack, and check that it matches the expected -// value, to avoid having to pass a pointer from Go, which would escape to the heap. - -int EVP_AEAD_CTX_seal_wrapper(const GO_EVP_AEAD_CTX *ctx, uint8_t *out, - size_t exp_out_len, - const uint8_t *nonce, size_t nonce_len, - const uint8_t *in, size_t in_len, - const uint8_t *ad, size_t ad_len) { - size_t out_len; - int ok = _goboringcrypto_EVP_AEAD_CTX_seal(ctx, out, &out_len, exp_out_len, - nonce, nonce_len, in, in_len, ad, ad_len); - if (out_len != exp_out_len) { - return 0; - } - return ok; -}; - -int EVP_AEAD_CTX_open_wrapper(const GO_EVP_AEAD_CTX *ctx, uint8_t *out, - size_t exp_out_len, - const uint8_t *nonce, size_t nonce_len, - const uint8_t *in, size_t in_len, - const uint8_t *ad, size_t ad_len) { - size_t out_len; - int ok = _goboringcrypto_EVP_AEAD_CTX_open(ctx, out, &out_len, exp_out_len, - nonce, nonce_len, in, in_len, ad, ad_len); - if (out_len != exp_out_len) { - return 0; - } - return ok; -}; - +#cgo noescape _goboringcrypto_EVP_AEAD_CTX_seal +#cgo nocallback _goboringcrypto_EVP_AEAD_CTX_seal +#cgo noescape _goboringcrypto_EVP_AEAD_CTX_open +#cgo nocallback _goboringcrypto_EVP_AEAD_CTX_open */ import "C" import ( @@ -318,15 +289,16 @@ func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { panic("cipher: invalid buffer overlap") } - outLen := C.size_t(len(plaintext) + gcmTagSize) - ok := C.EVP_AEAD_CTX_seal_wrapper( + var outLen C.size_t + expOutLen := C.size_t(len(plaintext) + gcmTagSize) + ok := C._goboringcrypto_EVP_AEAD_CTX_seal( &g.ctx, - (*C.uint8_t)(unsafe.Pointer(&dst[n])), outLen, + (*C.uint8_t)(unsafe.Pointer(&dst[n])), &outLen, expOutLen, base(nonce), C.size_t(len(nonce)), base(plaintext), C.size_t(len(plaintext)), base(additionalData), C.size_t(len(additionalData))) runtime.KeepAlive(g) - if ok == 0 { + if ok == 0 || outLen != expOutLen { panic(fail("EVP_AEAD_CTX_seal")) } return dst[:n+int(outLen)] @@ -357,15 +329,16 @@ func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, er panic("cipher: invalid buffer overlap") } - outLen := C.size_t(len(ciphertext) - gcmTagSize) - ok := C.EVP_AEAD_CTX_open_wrapper( + var outLen C.size_t + expOutLen := C.size_t(len(ciphertext) - gcmTagSize) + ok := C._goboringcrypto_EVP_AEAD_CTX_open( &g.ctx, - base(dst[n:]), outLen, + base(dst[n:]), &outLen, expOutLen, base(nonce), C.size_t(len(nonce)), base(ciphertext), C.size_t(len(ciphertext)), base(additionalData), C.size_t(len(additionalData))) runtime.KeepAlive(g) - if ok == 0 { + if ok == 0 || outLen != expOutLen { return nil, errOpen } return dst[:n+int(outLen)], nil