crypto/rand: batch large random reads on js

CL 463975 replaced the use of the NodeJS crypto.randomFillSync API
with a direct call to crypto.getRandomValues. This function rejects
any requests to fill a buffer larger than 65536 bytes, so we need to
batch reads larger than this size. This reuses the batching
functions used on other platforms to perform this batching.

Fixes #58145

Change-Id: Ic0acf3be7c9e994bc345d6614867c9b0c47bd26d
Reviewed-on: https://go-review.googlesource.com/c/go/+/463993
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
Auto-Submit: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
diff --git a/src/crypto/rand/rand_js.go b/src/crypto/rand/rand_js.go
index 91e69fa..39e8023 100644
--- a/src/crypto/rand/rand_js.go
+++ b/src/crypto/rand/rand_js.go
@@ -8,8 +8,15 @@
 
 import "syscall/js"
 
+// The maximum buffer size for crypto.getRandomValues is 65536 bytes.
+// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#exceptions
+const maxGetRandomRead = 1 << 16
+
+var batchedGetRandom func([]byte) error
+
 func init() {
 	Reader = &reader{}
+	batchedGetRandom = batched(getRandom, maxGetRandomRead)
 }
 
 var jsCrypto = js.Global().Get("crypto")
@@ -21,8 +28,15 @@
 type reader struct{}
 
 func (r *reader) Read(b []byte) (int, error) {
+	if err := batchedGetRandom(b); err != nil {
+		return 0, err
+	}
+	return len(b), nil
+}
+
+func getRandom(b []byte) error {
 	a := uint8Array.New(len(b))
 	jsCrypto.Call("getRandomValues", a)
 	js.CopyBytesToGo(b, a)
-	return len(b), nil
+	return nil
 }