diff --git a/src/crypto/internal/fips140/drbg/rand.go b/src/crypto/internal/fips140/drbg/rand.go index 949e74ac60..44284f93d8 100644 --- a/src/crypto/internal/fips140/drbg/rand.go +++ b/src/crypto/internal/fips140/drbg/rand.go @@ -124,14 +124,22 @@ func SetTestingReader(r io.Reader) { // [crypto/rand.Reader], used to recognize it when passed to // APIs that accept a rand io.Reader. // -// Any Reader that implements this interface is assumed to -// call [Read] as its Read method. -type DefaultReader interface{ defaultReader() } +// Any [io.Reader] that embeds this type is assumed to +// call [Read] as its [io.Reader.Read] method. +type DefaultReader struct{} + +func (d DefaultReader) defaultReader() {} + +// IsDefaultReader reports whether the r embeds the [DefaultReader] type. +func IsDefaultReader(r io.Reader) bool { + _, ok := r.(interface{ defaultReader() }) + return ok +} // ReadWithReader uses Reader to fill b with cryptographically secure random // bytes. It is intended for use in APIs that expose a rand io.Reader. func ReadWithReader(r io.Reader, b []byte) error { - if _, ok := r.(DefaultReader); ok { + if IsDefaultReader(r) { Read(b) return nil } diff --git a/src/crypto/internal/fips140only/fips140only.go b/src/crypto/internal/fips140only/fips140only.go index 1b0a4be6ba..a8d840b170 100644 --- a/src/crypto/internal/fips140only/fips140only.go +++ b/src/crypto/internal/fips140only/fips140only.go @@ -6,12 +6,10 @@ package fips140only import ( "crypto/fips140" - "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha256" "crypto/internal/fips140/sha3" "crypto/internal/fips140/sha512" "hash" - "io" ) // Enforced reports whether FIPS 140-only mode is enabled and enforced, in which non-approved @@ -28,8 +26,3 @@ func ApprovedHash(h hash.Hash) bool { return false } } - -func ApprovedRandomReader(r io.Reader) bool { - _, ok := r.(drbg.DefaultReader) - return ok -} diff --git a/src/crypto/internal/fips140only/random_fips140v1.0.go b/src/crypto/internal/fips140only/random_fips140v1.0.go new file mode 100644 index 0000000000..f402f308bc --- /dev/null +++ b/src/crypto/internal/fips140only/random_fips140v1.0.go @@ -0,0 +1,17 @@ +// Copyright 2026 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 fips140v1.0 || fips140v1.26 + +package fips140only + +import ( + "crypto/internal/fips140/drbg" + "io" +) + +func ApprovedRandomReader(r io.Reader) bool { + _, ok := r.(drbg.DefaultReader) + return ok +} diff --git a/src/crypto/internal/fips140only/random_fips140v1.28.go b/src/crypto/internal/fips140only/random_fips140v1.28.go new file mode 100644 index 0000000000..b03b9ef537 --- /dev/null +++ b/src/crypto/internal/fips140only/random_fips140v1.28.go @@ -0,0 +1,16 @@ +// Copyright 2026 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 !(fips140v1.0 || fips140v1.26) + +package fips140only + +import ( + "crypto/internal/fips140/drbg" + "io" +) + +func ApprovedRandomReader(r io.Reader) bool { + return drbg.IsDefaultReader(r) +} diff --git a/src/crypto/internal/rand/rand.go b/src/crypto/internal/rand/rand.go index 29648b9f38..00e736a579 100644 --- a/src/crypto/internal/rand/rand.go +++ b/src/crypto/internal/rand/rand.go @@ -13,9 +13,11 @@ import ( _ "unsafe" ) -type reader struct { - drbg.DefaultReader -} +// defaultReader aliases [drbg.DefaultReader], so that [reader] +// does not have an exported DefaultReader field. +type defaultReader = drbg.DefaultReader + +type reader struct{ defaultReader } func (r reader) Read(b []byte) (n int, err error) { if boring.Enabled { @@ -63,11 +65,3 @@ func CustomReader(r io.Reader) io.Reader { } return Reader } - -// IsDefaultReader reports whether r is the default [crypto/rand.Reader]. -// -// If true, the Read method of r can be assumed to call [drbg.Read]. -func IsDefaultReader(r io.Reader) bool { - _, ok := r.(drbg.DefaultReader) - return ok -} diff --git a/src/crypto/internal/rand/random_fips140v1.0.go b/src/crypto/internal/rand/random_fips140v1.0.go new file mode 100644 index 0000000000..7314157443 --- /dev/null +++ b/src/crypto/internal/rand/random_fips140v1.0.go @@ -0,0 +1,20 @@ +// Copyright 2026 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 fips140v1.0 || fips140v1.26 + +package rand + +import ( + "crypto/internal/fips140/drbg" + "io" +) + +// IsDefaultReader reports whether r is the default [crypto/rand.Reader]. +// +// If true, the Read method of r can be assumed to call [drbg.Read]. +func IsDefaultReader(r io.Reader) bool { + _, ok := r.(drbg.DefaultReader) + return ok +} diff --git a/src/crypto/internal/rand/random_fips140v1.28.go b/src/crypto/internal/rand/random_fips140v1.28.go new file mode 100644 index 0000000000..b9dafecff6 --- /dev/null +++ b/src/crypto/internal/rand/random_fips140v1.28.go @@ -0,0 +1,19 @@ +// Copyright 2026 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 !(fips140v1.0 || fips140v1.26) + +package rand + +import ( + "crypto/internal/fips140/drbg" + "io" +) + +// IsDefaultReader reports whether r is the default [crypto/rand.Reader]. +// +// If true, the Read method of r can be assumed to call [drbg.Read]. +func IsDefaultReader(r io.Reader) bool { + return drbg.IsDefaultReader(r) +} diff --git a/src/crypto/rand/rand_test.go b/src/crypto/rand/rand_test.go index 3bb3d5f1ac..13e4a65db2 100644 --- a/src/crypto/rand/rand_test.go +++ b/src/crypto/rand/rand_test.go @@ -8,10 +8,12 @@ import ( "bytes" "compress/flate" "crypto/internal/cryptotest" + "crypto/internal/rand" "errors" "internal/testenv" "io" "os" + "reflect" "sync" "testing" ) @@ -213,3 +215,25 @@ func benchmarkRead(b *testing.B, size int) { } } } + +func TestDefaultReader(t *testing.T) { + if !rand.IsDefaultReader(Reader) { + t.Error("rand.IsDefaultReader(Reader) == False") + } + + typ := reflect.ValueOf(Reader).Type() + for method := range typ.Methods() { + if method.Name == "Read" { + continue + } + if method.IsExported() { + t.Fatal("unexpected exported method") + } + } + + for field := range typ.Fields() { + if field.IsExported() { + t.Fatal("unexpected exported field") + } + } +}