diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/crypto/rand | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/crypto/rand')
-rw-r--r-- | src/crypto/rand/example_test.go | 28 | ||||
-rw-r--r-- | src/crypto/rand/rand.go | 44 | ||||
-rw-r--r-- | src/crypto/rand/rand_batched_test.go | 75 | ||||
-rw-r--r-- | src/crypto/rand/rand_getentropy.go | 14 | ||||
-rw-r--r-- | src/crypto/rand/rand_getrandom.go | 48 | ||||
-rw-r--r-- | src/crypto/rand/rand_js.go | 28 | ||||
-rw-r--r-- | src/crypto/rand/rand_plan9.go | 87 | ||||
-rw-r--r-- | src/crypto/rand/rand_test.go | 43 | ||||
-rw-r--r-- | src/crypto/rand/rand_unix.go | 87 | ||||
-rw-r--r-- | src/crypto/rand/rand_windows.go | 26 | ||||
-rw-r--r-- | src/crypto/rand/util.go | 99 | ||||
-rw-r--r-- | src/crypto/rand/util_test.go | 149 |
12 files changed, 728 insertions, 0 deletions
diff --git a/src/crypto/rand/example_test.go b/src/crypto/rand/example_test.go new file mode 100644 index 0000000..ed18647 --- /dev/null +++ b/src/crypto/rand/example_test.go @@ -0,0 +1,28 @@ +// Copyright 2011 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_test + +import ( + "bytes" + "crypto/rand" + "fmt" +) + +// This example reads 10 cryptographically secure pseudorandom numbers from +// rand.Reader and writes them to a byte slice. +func ExampleRead() { + c := 10 + b := make([]byte, c) + _, err := rand.Read(b) + if err != nil { + fmt.Println("error:", err) + return + } + // The slice should now contain random bytes instead of only zeroes. + fmt.Println(bytes.Equal(b, make([]byte, c))) + + // Output: + // false +} diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go new file mode 100644 index 0000000..af85b96 --- /dev/null +++ b/src/crypto/rand/rand.go @@ -0,0 +1,44 @@ +// Copyright 2010 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 implements a cryptographically secure +// random number generator. +package rand + +import "io" + +// Reader is a global, shared instance of a cryptographically +// secure random number generator. +// +// On Linux, FreeBSD, Dragonfly 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 RtlGenRandom API. +// On Wasm, Reader uses the Web Crypto API. +var Reader io.Reader + +// Read is a helper function that calls Reader.Read using io.ReadFull. +// On return, n == len(b) if and only if err == nil. +func Read(b []byte) (n int, err error) { + return io.ReadFull(Reader, b) +} + +// batched returns a function that calls f to populate a []byte by chunking it +// into subslices of, at most, readMax bytes. +func batched(f func([]byte) error, readMax int) func([]byte) error { + return func(out []byte) error { + for len(out) > 0 { + read := len(out) + if read > readMax { + read = readMax + } + if err := f(out[:read]); err != nil { + return err + } + out = out[read:] + } + return nil + } +} diff --git a/src/crypto/rand/rand_batched_test.go b/src/crypto/rand/rand_batched_test.go new file mode 100644 index 0000000..02f4893 --- /dev/null +++ b/src/crypto/rand/rand_batched_test.go @@ -0,0 +1,75 @@ +// Copyright 2014 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 unix + +package rand + +import ( + "bytes" + "errors" + prand "math/rand" + "testing" +) + +func TestBatched(t *testing.T) { + fillBatched := batched(func(p []byte) error { + for i := range p { + p[i] = byte(i) + } + return nil + }, 5) + + p := make([]byte, 13) + if err := fillBatched(p); err != nil { + t.Fatalf("batched function returned error: %s", err) + } + expected := []byte{0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2} + if !bytes.Equal(expected, p) { + t.Errorf("incorrect batch result: got %x, want %x", p, expected) + } +} + +func TestBatchedBuffering(t *testing.T) { + backingStore := make([]byte, 1<<23) + prand.Read(backingStore) + backingMarker := backingStore[:] + output := make([]byte, len(backingStore)) + outputMarker := output[:] + + fillBatched := batched(func(p []byte) error { + n := copy(p, backingMarker) + backingMarker = backingMarker[n:] + return nil + }, 731) + + for len(outputMarker) > 0 { + max := 9200 + if max > len(outputMarker) { + max = len(outputMarker) + } + howMuch := prand.Intn(max + 1) + if err := fillBatched(outputMarker[:howMuch]); err != nil { + t.Fatalf("batched function returned error: %s", err) + } + outputMarker = outputMarker[howMuch:] + } + if !bytes.Equal(backingStore, output) { + t.Error("incorrect batch result") + } +} + +func TestBatchedError(t *testing.T) { + b := batched(func(p []byte) error { return errors.New("failure") }, 5) + if b(make([]byte, 13)) == nil { + t.Fatal("batched function should have returned an error") + } +} + +func TestBatchedEmpty(t *testing.T) { + b := batched(func(p []byte) error { return errors.New("failure") }, 5) + if b(make([]byte, 0)) != nil { + t.Fatal("empty slice should always return successful") + } +} diff --git a/src/crypto/rand/rand_getentropy.go b/src/crypto/rand/rand_getentropy.go new file mode 100644 index 0000000..68f921b --- /dev/null +++ b/src/crypto/rand/rand_getentropy.go @@ -0,0 +1,14 @@ +// Copyright 2016 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) || openbsd + +package rand + +import "internal/syscall/unix" + +func init() { + // getentropy(2) returns a maximum of 256 bytes per call + altGetRandom = batched(unix.GetEntropy, 256) +} diff --git a/src/crypto/rand/rand_getrandom.go b/src/crypto/rand/rand_getrandom.go new file mode 100644 index 0000000..478aa5c --- /dev/null +++ b/src/crypto/rand/rand_getrandom.go @@ -0,0 +1,48 @@ +// Copyright 2014 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 linux || freebsd || dragonfly || solaris + +package rand + +import ( + "internal/syscall/unix" + "runtime" + "syscall" +) + +func init() { + var maxGetRandomRead int + switch runtime.GOOS { + case "linux", "android": + // Per the manpage: + // When reading from the urandom source, a maximum of 33554431 bytes + // is returned by a single call to getrandom() on systems where int + // has a size of 32 bits. + maxGetRandomRead = (1 << 25) - 1 + case "freebsd", "dragonfly", "solaris", "illumos": + maxGetRandomRead = 1 << 8 + default: + panic("no maximum specified for GetRandom") + } + altGetRandom = batched(getRandom, maxGetRandomRead) +} + +// If the kernel is too old to support the getrandom syscall(), +// unix.GetRandom will immediately return ENOSYS and we will then fall back to +// reading from /dev/urandom in rand_unix.go. unix.GetRandom caches the ENOSYS +// result so we only suffer the syscall overhead once in this case. +// If the kernel supports the getrandom() syscall, unix.GetRandom will block +// until the kernel has sufficient randomness (as we don't use GRND_NONBLOCK). +// In this case, unix.GetRandom will not return an error. +func getRandom(p []byte) error { + n, err := unix.GetRandom(p, 0) + if err != nil { + return err + } + if n != len(p) { + return syscall.EIO + } + return nil +} diff --git a/src/crypto/rand/rand_js.go b/src/crypto/rand/rand_js.go new file mode 100644 index 0000000..91e69fa --- /dev/null +++ b/src/crypto/rand/rand_js.go @@ -0,0 +1,28 @@ +// Copyright 2018 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 js && wasm + +package rand + +import "syscall/js" + +func init() { + Reader = &reader{} +} + +var jsCrypto = js.Global().Get("crypto") +var uint8Array = js.Global().Get("Uint8Array") + +// reader implements a pseudorandom generator +// using JavaScript crypto.getRandomValues method. +// See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues. +type reader struct{} + +func (r *reader) Read(b []byte) (int, error) { + a := uint8Array.New(len(b)) + jsCrypto.Call("getRandomValues", a) + js.CopyBytesToGo(b, a) + return len(b), nil +} diff --git a/src/crypto/rand/rand_plan9.go b/src/crypto/rand/rand_plan9.go new file mode 100644 index 0000000..5d0af09 --- /dev/null +++ b/src/crypto/rand/rand_plan9.go @@ -0,0 +1,87 @@ +// Copyright 2010 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. + +// Plan9 cryptographically secure pseudorandom number +// generator. + +package rand + +import ( + "crypto/aes" + "encoding/binary" + "io" + "os" + "sync" + "time" +) + +const randomDevice = "/dev/random" + +func init() { + Reader = &reader{} +} + +// reader is a new pseudorandom generator that seeds itself by +// reading from /dev/random. The Read method on the returned +// reader always returns the full amount asked for, or else it +// returns an error. The generator is a fast key erasure RNG. +type reader struct { + mu sync.Mutex + seeded sync.Once + seedErr error + key [32]byte +} + +func (r *reader) Read(b []byte) (n int, err error) { + r.seeded.Do(func() { + t := time.AfterFunc(time.Minute, func() { + println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") + }) + defer t.Stop() + entropy, err := os.Open(randomDevice) + if err != nil { + r.seedErr = err + return + } + _, r.seedErr = io.ReadFull(entropy, r.key[:]) + }) + if r.seedErr != nil { + return 0, r.seedErr + } + + r.mu.Lock() + blockCipher, err := aes.NewCipher(r.key[:]) + if err != nil { + r.mu.Unlock() + return 0, err + } + var ( + counter uint64 + block [aes.BlockSize]byte + ) + inc := func() { + counter++ + if counter == 0 { + panic("crypto/rand counter wrapped") + } + binary.LittleEndian.PutUint64(block[:], counter) + } + blockCipher.Encrypt(r.key[:aes.BlockSize], block[:]) + inc() + blockCipher.Encrypt(r.key[aes.BlockSize:], block[:]) + inc() + r.mu.Unlock() + + n = len(b) + for len(b) >= aes.BlockSize { + blockCipher.Encrypt(b[:aes.BlockSize], block[:]) + inc() + b = b[aes.BlockSize:] + } + if len(b) > 0 { + blockCipher.Encrypt(block[:], block[:]) + copy(b, block[:]) + } + return n, nil +} diff --git a/src/crypto/rand/rand_test.go b/src/crypto/rand/rand_test.go new file mode 100644 index 0000000..e45f58e --- /dev/null +++ b/src/crypto/rand/rand_test.go @@ -0,0 +1,43 @@ +// Copyright 2010 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 ( + "bytes" + "compress/flate" + "io" + "testing" +) + +func TestRead(t *testing.T) { + var n int = 4e6 + if testing.Short() { + n = 1e5 + } + b := make([]byte, n) + n, err := io.ReadFull(Reader, b) + if n != len(b) || err != nil { + t.Fatalf("ReadFull(buf) = %d, %s", n, err) + } + + var z bytes.Buffer + f, _ := flate.NewWriter(&z, 5) + f.Write(b) + f.Close() + if z.Len() < len(b)*99/100 { + t.Fatalf("Compressed %d -> %d", len(b), z.Len()) + } +} + +func TestReadEmpty(t *testing.T) { + n, err := Reader.Read(make([]byte, 0)) + if n != 0 || err != nil { + t.Fatalf("Read(make([]byte, 0)) = %d, %v", n, err) + } + n, err = Reader.Read(nil) + if n != 0 || err != nil { + t.Fatalf("Read(nil) = %d, %v", n, err) + } +} diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go new file mode 100644 index 0000000..40fce36 --- /dev/null +++ b/src/crypto/rand/rand_unix.go @@ -0,0 +1,87 @@ +// Copyright 2010 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 unix + +// Unix cryptographically secure pseudorandom number +// generator. + +package rand + +import ( + "crypto/internal/boring" + "errors" + "io" + "os" + "sync" + "sync/atomic" + "syscall" + "time" +) + +const urandomDevice = "/dev/urandom" + +func init() { + if boring.Enabled { + Reader = boring.RandReader + return + } + Reader = &reader{} +} + +// A reader satisfies reads by reading from urandomDevice +type reader struct { + f io.Reader + mu sync.Mutex + used atomic.Uint32 // Atomic: 0 - never used, 1 - used, but f == nil, 2 - used, and f != nil +} + +// altGetRandom if non-nil specifies an OS-specific function to get +// urandom-style randomness. +var altGetRandom func([]byte) (err error) + +func warnBlocked() { + println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel") +} + +func (r *reader) Read(b []byte) (n int, err error) { + boring.Unreachable() + if r.used.CompareAndSwap(0, 1) { + // First use of randomness. Start timer to warn about + // being blocked on entropy not being available. + t := time.AfterFunc(time.Minute, warnBlocked) + defer t.Stop() + } + if altGetRandom != nil && altGetRandom(b) == nil { + return len(b), nil + } + if r.used.Load() != 2 { + r.mu.Lock() + if r.used.Load() != 2 { + f, err := os.Open(urandomDevice) + if err != nil { + r.mu.Unlock() + return 0, err + } + r.f = hideAgainReader{f} + r.used.Store(2) + } + r.mu.Unlock() + } + return io.ReadFull(r.f, b) +} + +// hideAgainReader masks EAGAIN reads from /dev/urandom. +// See golang.org/issue/9205 +type hideAgainReader struct { + r io.Reader +} + +func (hr hideAgainReader) Read(p []byte) (n int, err error) { + n, err = hr.r.Read(p) + if errors.Is(err, syscall.EAGAIN) { + err = nil + } + return +} diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go new file mode 100644 index 0000000..6c0655c --- /dev/null +++ b/src/crypto/rand/rand_windows.go @@ -0,0 +1,26 @@ +// Copyright 2010 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. + +// Windows cryptographically secure pseudorandom number +// generator. + +package rand + +import ( + "internal/syscall/windows" +) + +func init() { Reader = &rngReader{} } + +type rngReader struct{} + +func (r *rngReader) Read(b []byte) (n int, err error) { + // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at + // most 1<<31-1 bytes at a time so that this works the same on 32-bit + // and 64-bit systems. + if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil { + return 0, err + } + return len(b), nil +} diff --git a/src/crypto/rand/util.go b/src/crypto/rand/util.go new file mode 100644 index 0000000..11b1a28 --- /dev/null +++ b/src/crypto/rand/util.go @@ -0,0 +1,99 @@ +// Copyright 2011 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 ( + "crypto/internal/randutil" + "errors" + "io" + "math/big" +) + +// Prime returns a number of the given bit length that is prime with high probability. +// Prime will return error for any error returned by rand.Read or if bits < 2. +func Prime(rand io.Reader, bits int) (*big.Int, error) { + if bits < 2 { + return nil, errors.New("crypto/rand: prime size must be at least 2-bit") + } + + randutil.MaybeReadByte(rand) + + b := uint(bits % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, (bits+7)/8) + p := new(big.Int) + + for { + if _, err := io.ReadFull(rand, bytes); err != nil { + return nil, err + } + + // Clear bits in the first byte to make sure the candidate has a size <= bits. + bytes[0] &= uint8(int(1<<b) - 1) + // Don't let the value be too small, i.e, set the most significant two bits. + // Setting the top two bits, rather than just the top bit, + // means that when two of these values are multiplied together, + // the result isn't ever one bit short. + if b >= 2 { + bytes[0] |= 3 << (b - 2) + } else { + // Here b==1, because b cannot be zero. + bytes[0] |= 1 + if len(bytes) > 1 { + bytes[1] |= 0x80 + } + } + // Make the value odd since an even number this large certainly isn't prime. + bytes[len(bytes)-1] |= 1 + + p.SetBytes(bytes) + if p.ProbablyPrime(20) { + return p, nil + } + } +} + +// Int returns a uniform random value in [0, max). It panics if max <= 0. +func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) { + if max.Sign() <= 0 { + panic("crypto/rand: argument to Int is <= 0") + } + n = new(big.Int) + n.Sub(max, n.SetUint64(1)) + // bitLen is the maximum bit length needed to encode a value < max. + bitLen := n.BitLen() + if bitLen == 0 { + // the only valid result is 0 + return + } + // k is the maximum byte length needed to encode a value < max. + k := (bitLen + 7) / 8 + // b is the number of bits in the most significant byte of max-1. + b := uint(bitLen % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, k) + + for { + _, err = io.ReadFull(rand, bytes) + if err != nil { + return nil, err + } + + // Clear bits in the first byte to increase the probability + // that the candidate is < max. + bytes[0] &= uint8(int(1<<b) - 1) + + n.SetBytes(bytes) + if n.Cmp(max) < 0 { + return + } + } +} diff --git a/src/crypto/rand/util_test.go b/src/crypto/rand/util_test.go new file mode 100644 index 0000000..9caf8e9 --- /dev/null +++ b/src/crypto/rand/util_test.go @@ -0,0 +1,149 @@ +// Copyright 2013 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_test + +import ( + "bytes" + "crypto/rand" + "fmt" + "io" + "math/big" + mathrand "math/rand" + "testing" + "time" +) + +// https://golang.org/issue/6849. +func TestPrimeSmall(t *testing.T) { + for n := 2; n < 10; n++ { + p, err := rand.Prime(rand.Reader, n) + if err != nil { + t.Fatalf("Can't generate %d-bit prime: %v", n, err) + } + if p.BitLen() != n { + t.Fatalf("%v is not %d-bit", p, n) + } + if !p.ProbablyPrime(32) { + t.Fatalf("%v is not prime", p) + } + } +} + +// Test that passing bits < 2 causes Prime to return nil, error +func TestPrimeBitsLt2(t *testing.T) { + if p, err := rand.Prime(rand.Reader, 1); p != nil || err == nil { + t.Errorf("Prime should return nil, error when called with bits < 2") + } +} + +func TestPrimeNondeterministic(t *testing.T) { + r := mathrand.New(mathrand.NewSource(42)) + p0, err := rand.Prime(r, 32) + if err != nil { + t.Fatal(err) + } + for i := 0; i < 128; i++ { + r.Seed(42) + p, err := rand.Prime(r, 32) + if err != nil { + t.Fatal(err) + } + if p.Cmp(p0) != 0 { + return + } + } + t.Error("Prime always generated the same prime given the same input") +} + +func TestInt(t *testing.T) { + // start at 128 so the case of (max.BitLen() % 8) == 0 is covered + for n := 128; n < 140; n++ { + b := new(big.Int).SetInt64(int64(n)) + if i, err := rand.Int(rand.Reader, b); err != nil { + t.Fatalf("Can't generate random value: %v, %v", i, err) + } + } +} + +type countingReader struct { + r io.Reader + n int +} + +func (r *countingReader) Read(p []byte) (n int, err error) { + n, err = r.r.Read(p) + r.n += n + return n, err +} + +// Test that Int reads only the necessary number of bytes from the reader for +// max at each bit length +func TestIntReads(t *testing.T) { + for i := 0; i < 32; i++ { + max := int64(1 << uint64(i)) + t.Run(fmt.Sprintf("max=%d", max), func(t *testing.T) { + reader := &countingReader{r: rand.Reader} + + _, err := rand.Int(reader, big.NewInt(max)) + if err != nil { + t.Fatalf("Can't generate random value: %d, %v", max, err) + } + expected := (i + 7) / 8 + if reader.n != expected { + t.Errorf("Int(reader, %d) should read %d bytes, but it read: %d", max, expected, reader.n) + } + }) + } +} + +// Test that Int does not mask out valid return values +func TestIntMask(t *testing.T) { + for max := 1; max <= 256; max++ { + t.Run(fmt.Sprintf("max=%d", max), func(t *testing.T) { + for i := 0; i < max; i++ { + if testing.Short() && i == 0 { + i = max - 1 + } + var b bytes.Buffer + b.WriteByte(byte(i)) + n, err := rand.Int(&b, big.NewInt(int64(max))) + if err != nil { + t.Fatalf("Can't generate random value: %d, %v", max, err) + } + if n.Int64() != int64(i) { + t.Errorf("Int(reader, %d) should have returned value of %d, but it returned: %v", max, i, n) + } + } + }) + } +} + +func testIntPanics(t *testing.T, b *big.Int) { + defer func() { + if err := recover(); err == nil { + t.Errorf("Int should panic when called with max <= 0: %v", b) + } + }() + rand.Int(rand.Reader, b) +} + +// Test that passing a new big.Int as max causes Int to panic +func TestIntEmptyMaxPanics(t *testing.T) { + b := new(big.Int) + testIntPanics(t, b) +} + +// Test that passing a negative value as max causes Int to panic +func TestIntNegativeMaxPanics(t *testing.T) { + b := new(big.Int).SetInt64(int64(-1)) + testIntPanics(t, b) +} + +func BenchmarkPrime(b *testing.B) { + r := mathrand.New(mathrand.NewSource(time.Now().UnixNano())) + for i := 0; i < b.N; i++ { + rand.Prime(r, 1024) + } +} |