]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/rand: use arc4random(3) on macOS and iOS
authorFilippo Valsorda <filippo@golang.org>
Wed, 6 Mar 2024 23:18:02 +0000 (00:18 +0100)
committerGopher Robot <gobot@golang.org>
Tue, 19 Mar 2024 20:02:21 +0000 (20:02 +0000)
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 <filippo@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Mauri de Souza Meneguzzo <mauri870@gmail.com>
src/crypto/rand/rand.go
src/crypto/rand/rand_darwin.go [new file with mode: 0644]
src/crypto/rand/rand_getentropy.go
src/crypto/rand/rand_test.go
src/internal/syscall/unix/arc4random_darwin.go [new file with mode: 0644]
src/internal/syscall/unix/asm_darwin.s
src/internal/syscall/unix/getentropy_darwin.go [deleted file]
src/internal/syscall/unix/getentropy_darwin.s [deleted file]

index d0dcc7cc71fc0c9b35c12fd25a2a551e7253044b..d16d7a1c9c3fe4ce7e1e9578bc905c48e4df4032 100644 (file)
@@ -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 (file)
index 0000000..363ad69
--- /dev/null
@@ -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 }
+}
index 210250411eab9a98ff73bbe85a12fd4b123432eb..855716c83dd08474c6f9949a23a13d3905ec886b 100644 (file)
@@ -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)
 }
index e45f58e4e7a1982374bb0ab0327f11337983965d..bbd4a86cda1b245f476c8592665369c7002de44e 100644 (file)
@@ -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 (file)
index 0000000..a78204a
--- /dev/null
@@ -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)
+}
index 10d16ce87f0946189db3902f4ddff33f21f8aa9a..0b8efb1506d2bcd57cae308a3570c1bbfb1fa947 100644 (file)
@@ -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 (file)
index 834099f..0000000
+++ /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 (file)
index f41e0fe..0000000
+++ /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)