From 99522de1c38e4915e061cd2dac7d34ee888c8318 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Thu, 7 Mar 2024 00:18:02 +0100 Subject: [PATCH] crypto/rand: use arc4random(3) on macOS and iOS MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit It's been good since Sierra: it never fails, it's faster, it's available on iOS (see #47812), and it still handles forks and reseeding. On a M2 with macOS 14.3.1: │ sec/op │ sec/op vs base │ Read/32-8 413.7n ± 3% 249.7n ± 3% -39.65% (p=0.000 n=10) Read/4K-8 7.097µ ± 6% 1.261µ ± 2% -82.24% (p=0.000 n=10) │ B/s │ B/s vs base │ Read/32-8 73.76Mi ± 3% 122.25Mi ± 3% +65.73% (p=0.000 n=10) Read/4K-8 550.5Mi ± 6% 3099.0Mi ± 2% +462.99% (p=0.000 n=10) arc4random(3) would be a good replacement for getentropy(2) on FreeBSD and NetBSD as well, but we don't get as easy access to libc there. Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64-longtest,gotip-darwin-amd64-nocgo,gotip-darwin-arm64_13,gotip-darwin-amd64_11,gotip-darwin-amd64_12,gotip-darwin-amd64_13,gotip-darwin-amd64_14 Change-Id: Ia76824853be92b4d1786e23592a1d2ef24d8907d Reviewed-on: https://go-review.googlesource.com/c/go/+/569655 Auto-Submit: Filippo Valsorda Reviewed-by: Cherry Mui LUCI-TryBot-Result: Go LUCI Reviewed-by: Emmanuel Odeke Reviewed-by: Roland Shoemaker Reviewed-by: Mauri de Souza Meneguzzo --- src/crypto/rand/rand.go | 15 +++++----- src/crypto/rand/rand_darwin.go | 19 +++++++++++++ src/crypto/rand/rand_getentropy.go | 4 +-- src/crypto/rand/rand_test.go | 19 +++++++++++++ .../syscall/unix/arc4random_darwin.go | 24 ++++++++++++++++ src/internal/syscall/unix/asm_darwin.s | 1 + .../syscall/unix/getentropy_darwin.go | 28 ------------------- src/internal/syscall/unix/getentropy_darwin.s | 9 ------ 8 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 src/crypto/rand/rand_darwin.go create mode 100644 src/internal/syscall/unix/arc4random_darwin.go delete mode 100644 src/internal/syscall/unix/getentropy_darwin.go delete mode 100644 src/internal/syscall/unix/getentropy_darwin.s diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index d0dcc7cc71..d16d7a1c9c 100644 --- a/src/crypto/rand/rand.go +++ b/src/crypto/rand/rand.go @@ -11,13 +11,14 @@ import "io" // Reader is a global, shared instance of a cryptographically // secure random number generator. // -// On Linux, FreeBSD, Dragonfly, NetBSD and Solaris, Reader uses getrandom(2) if -// available, /dev/urandom otherwise. -// On OpenBSD and macOS, Reader uses getentropy(2). -// On other Unix-like systems, Reader reads from /dev/urandom. -// On Windows systems, Reader uses the ProcessPrng API. -// On JS/Wasm, Reader uses the Web Crypto API. -// On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1. +// - On Linux, FreeBSD, Dragonfly, and Solaris, Reader uses getrandom(2) +// if available, and /dev/urandom otherwise. +// - On macOS and iOS, Reader uses arc4random_buf(3). +// - On OpenBSD and NetBSD, Reader uses getentropy(2). +// - On other Unix-like systems, Reader reads from /dev/urandom. +// - On Windows, Reader uses the ProcessPrng API. +// - On js/wasm, Reader uses the Web Crypto API. +// - On wasip1/wasm, Reader uses random_get from wasi_snapshot_preview1. var Reader io.Reader // Read is a helper function that calls Reader.Read using io.ReadFull. diff --git a/src/crypto/rand/rand_darwin.go b/src/crypto/rand/rand_darwin.go new file mode 100644 index 0000000000..363ad69ec4 --- /dev/null +++ b/src/crypto/rand/rand_darwin.go @@ -0,0 +1,19 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import "internal/syscall/unix" + +func init() { + // arc4random_buf is the recommended application CSPRNG, accepts buffers of + // any size, and never returns an error. + // + // "The subsystem is re-seeded from the kernel random number subsystem on a + // regular basis, and also upon fork(2)." - arc4random(3) + // + // Note that despite its legacy name, it uses a secure CSPRNG (not RC4) in + // all supported macOS versions. + altGetRandom = func(b []byte) error { unix.ARC4Random(b); return nil } +} diff --git a/src/crypto/rand/rand_getentropy.go b/src/crypto/rand/rand_getentropy.go index 210250411e..855716c83d 100644 --- a/src/crypto/rand/rand_getentropy.go +++ b/src/crypto/rand/rand_getentropy.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (darwin && !ios) || openbsd || netbsd +//go:build openbsd || netbsd package rand import "internal/syscall/unix" func init() { - // getentropy(2) returns a maximum of 256 bytes per call + // getentropy(2) returns a maximum of 256 bytes per call. altGetRandom = batched(unix.GetEntropy, 256) } diff --git a/src/crypto/rand/rand_test.go b/src/crypto/rand/rand_test.go index e45f58e4e7..bbd4a86cda 100644 --- a/src/crypto/rand/rand_test.go +++ b/src/crypto/rand/rand_test.go @@ -41,3 +41,22 @@ func TestReadEmpty(t *testing.T) { t.Fatalf("Read(nil) = %d, %v", n, err) } } + +func BenchmarkRead(b *testing.B) { + b.Run("32", func(b *testing.B) { + benchmarkRead(b, 32) + }) + b.Run("4K", func(b *testing.B) { + benchmarkRead(b, 4<<10) + }) +} + +func benchmarkRead(b *testing.B, size int) { + b.SetBytes(int64(size)) + buf := make([]byte, size) + for i := 0; i < b.N; i++ { + if _, err := Read(buf); err != nil { + b.Fatal(err) + } + } +} diff --git a/src/internal/syscall/unix/arc4random_darwin.go b/src/internal/syscall/unix/arc4random_darwin.go new file mode 100644 index 0000000000..a78204a355 --- /dev/null +++ b/src/internal/syscall/unix/arc4random_darwin.go @@ -0,0 +1,24 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import ( + "internal/abi" + "unsafe" +) + +//go:cgo_import_dynamic libc_arc4random_buf arc4random_buf "/usr/lib/libSystem.B.dylib" + +func libc_arc4random_buf_trampoline() + +// ARC4Random calls the macOS arc4random_buf(3) function. +func ARC4Random(p []byte) { + // macOS 11 and 12 abort if length is 0. + if len(p) == 0 { + return + } + syscall_syscall(abi.FuncPCABI0(libc_arc4random_buf_trampoline), + uintptr(unsafe.Pointer(unsafe.SliceData(p))), uintptr(len(p)), 0) +} diff --git a/src/internal/syscall/unix/asm_darwin.s b/src/internal/syscall/unix/asm_darwin.s index 10d16ce87f..0b8efb1506 100644 --- a/src/internal/syscall/unix/asm_darwin.s +++ b/src/internal/syscall/unix/asm_darwin.s @@ -4,6 +4,7 @@ #include "textflag.h" +TEXT ·libc_arc4random_buf_trampoline(SB),NOSPLIT,$0-0; JMP libc_arc4random_buf(SB) TEXT ·libc_getaddrinfo_trampoline(SB),NOSPLIT,$0-0; JMP libc_getaddrinfo(SB) TEXT ·libc_freeaddrinfo_trampoline(SB),NOSPLIT,$0-0; JMP libc_freeaddrinfo(SB) TEXT ·libc_getnameinfo_trampoline(SB),NOSPLIT,$0-0; JMP libc_getnameinfo(SB) diff --git a/src/internal/syscall/unix/getentropy_darwin.go b/src/internal/syscall/unix/getentropy_darwin.go deleted file mode 100644 index 834099ffed..0000000000 --- a/src/internal/syscall/unix/getentropy_darwin.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build darwin && !ios - -package unix - -import ( - "internal/abi" - "unsafe" -) - -//go:cgo_import_dynamic libc_getentropy getentropy "/usr/lib/libSystem.B.dylib" - -func libc_getentropy_trampoline() - -// GetEntropy calls the macOS getentropy system call. -func GetEntropy(p []byte) error { - _, _, errno := syscall_syscall(abi.FuncPCABI0(libc_getentropy_trampoline), - uintptr(unsafe.Pointer(&p[0])), - uintptr(len(p)), - 0) - if errno != 0 { - return errno - } - return nil -} diff --git a/src/internal/syscall/unix/getentropy_darwin.s b/src/internal/syscall/unix/getentropy_darwin.s deleted file mode 100644 index f41e0fe97b..0000000000 --- a/src/internal/syscall/unix/getentropy_darwin.s +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build darwin && !ios - -#include "textflag.h" - -TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0; JMP libc_getentropy(SB) -- 2.48.1